mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 22:39:46 +00:00
Feat bulk locking (#1422)
* Feat: Multi-lock capabilities add to config * wip: RPC.rs fixes, new combinatoric * wip: changes * chore: More things that are bulk * fix: Saving * chore: Remove a dyn object * chore: Add tests + remove unused * Fix/feat bulk locking (#1427) * fix: health check * fix: start/stop service * fix: install/uninstall services * chore: Fix the notifications * fix: Version * fix: Version as serde * chore: Update to latest patch db * chore: Change the htLock to something that makes more sense * chore: Fix the rest of the ht * "chore: More ht_lock":
This commit is contained in:
@@ -85,7 +85,7 @@ num_enum = "0.5.4"
|
|||||||
openssh-keys = "0.5.0"
|
openssh-keys = "0.5.0"
|
||||||
openssl = { version = "0.10.36", features = ["vendored"] }
|
openssl = { version = "0.10.36", features = ["vendored"] }
|
||||||
patch-db = { version = "*", path = "../patch-db/patch-db", features = [
|
patch-db = { version = "*", path = "../patch-db/patch-db", features = [
|
||||||
"trace",
|
"trace"
|
||||||
] }
|
] }
|
||||||
pbkdf2 = "0.9.0"
|
pbkdf2 = "0.9.0"
|
||||||
pin-project = "1.0.8"
|
pin-project = "1.0.8"
|
||||||
|
|||||||
@@ -240,7 +240,8 @@ impl BackupActions {
|
|||||||
.get(db, true)
|
.get(db, true)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
reconfigure_dependents_with_live_pointers(ctx, db, &entry).await?;
|
let receipts = crate::config::ConfigReceipts::new(db).await?;
|
||||||
|
reconfigure_dependents_with_live_pointers(ctx, db, &receipts, &entry).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,11 @@ async fn inner_main(cfg_path: Option<&str>) -> Result<Option<Shutdown>, Error> {
|
|||||||
.expect("send shutdown signal");
|
.expect("send shutdown signal");
|
||||||
});
|
});
|
||||||
|
|
||||||
rpc_ctx.set_nginx_conf(&mut rpc_ctx.db.handle()).await?;
|
let mut db = rpc_ctx.db.handle();
|
||||||
|
let receipts = embassy::context::rpc::RpcSetNginxReceipts::new(&mut db).await?;
|
||||||
|
|
||||||
|
rpc_ctx.set_nginx_conf(&mut db, receipts).await?;
|
||||||
|
drop(db);
|
||||||
let auth = auth(rpc_ctx.clone());
|
let auth = auth(rpc_ctx.clone());
|
||||||
let ctx = rpc_ctx.clone();
|
let ctx = rpc_ctx.clone();
|
||||||
let server = rpc_server!({
|
let server = rpc_server!({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use color_eyre::eyre::eyre;
|
|||||||
use futures::future::{BoxFuture, FutureExt};
|
use futures::future::{BoxFuture, FutureExt};
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use patch_db::{DbHandle, LockType};
|
use patch_db::{DbHandle, LockReceipt, LockTarget, LockTargetId, LockType, Verifier};
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rpc_toolkit::command;
|
use rpc_toolkit::command;
|
||||||
@@ -14,17 +14,20 @@ use serde_json::Value;
|
|||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
use crate::db::model::CurrentDependencyInfo;
|
|
||||||
use crate::db::util::WithRevision;
|
|
||||||
use crate::dependencies::{
|
use crate::dependencies::{
|
||||||
add_dependent_to_current_dependents_lists, break_transitive, heal_all_dependents_transitive,
|
add_dependent_to_current_dependents_lists, break_transitive, heal_all_dependents_transitive,
|
||||||
BreakageRes, DependencyError, DependencyErrors, TaggedDependencyError,
|
BreakageRes, DependencyConfig, DependencyError, DependencyErrors, TaggedDependencyError,
|
||||||
};
|
};
|
||||||
use crate::install::cleanup::remove_from_current_dependents_lists;
|
|
||||||
use crate::s9pk::manifest::{Manifest, PackageId};
|
use crate::s9pk::manifest::{Manifest, PackageId};
|
||||||
use crate::util::display_none;
|
|
||||||
use crate::util::serde::{display_serializable, parse_stdin_deserializable, IoFormat};
|
use crate::util::serde::{display_serializable, parse_stdin_deserializable, IoFormat};
|
||||||
use crate::{Error, ResultExt as _};
|
use crate::Error;
|
||||||
|
use crate::{db::model::CurrentDependencyInfo, dependencies::DependencyReceipt};
|
||||||
|
use crate::{db::util::WithRevision, dependencies::Dependencies};
|
||||||
|
use crate::{
|
||||||
|
dependencies::BreakTransitiveReceipts,
|
||||||
|
install::cleanup::{remove_from_current_dependents_lists, UpdateDependencyReceipts},
|
||||||
|
};
|
||||||
|
use crate::{dependencies::TryHealReceipts, util::display_none};
|
||||||
|
|
||||||
pub mod action;
|
pub mod action;
|
||||||
pub mod spec;
|
pub mod spec;
|
||||||
@@ -33,8 +36,11 @@ pub mod util;
|
|||||||
pub use spec::{ConfigSpec, Defaultable};
|
pub use spec::{ConfigSpec, Defaultable};
|
||||||
use util::NumRange;
|
use util::NumRange;
|
||||||
|
|
||||||
use self::action::ConfigRes;
|
|
||||||
use self::spec::{PackagePointerSpec, ValueSpecPointer};
|
use self::spec::{PackagePointerSpec, ValueSpecPointer};
|
||||||
|
use self::{
|
||||||
|
action::{ConfigActions, ConfigRes},
|
||||||
|
spec::ConfigPointerReceipts,
|
||||||
|
};
|
||||||
|
|
||||||
pub type Config = serde_json::Map<String, Value>;
|
pub type Config = serde_json::Map<String, Value>;
|
||||||
pub trait TypeOf {
|
pub trait TypeOf {
|
||||||
@@ -163,6 +169,56 @@ pub fn config(#[arg] id: PackageId) -> Result<PackageId, Error> {
|
|||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ConfigGetReceipts {
|
||||||
|
manifest_volumes: LockReceipt<crate::volume::Volumes, ()>,
|
||||||
|
manifest_version: LockReceipt<crate::util::Version, ()>,
|
||||||
|
manifest_config: LockReceipt<Option<ConfigActions>, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigGetReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle, id: &PackageId) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks, id);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
locks: &mut Vec<LockTargetId>,
|
||||||
|
id: &PackageId,
|
||||||
|
) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
|
||||||
|
let manifest_version = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.map(|x| x.manifest().version())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let manifest_volumes = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.map(|x| x.manifest().volumes())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let manifest_config = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.map(|x| x.manifest().config())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
manifest_volumes: manifest_volumes.verify(skeleton_key)?,
|
||||||
|
manifest_version: manifest_version.verify(skeleton_key)?,
|
||||||
|
manifest_config: manifest_config.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[command(display(display_serializable))]
|
#[command(display(display_serializable))]
|
||||||
#[instrument(skip(ctx))]
|
#[instrument(skip(ctx))]
|
||||||
pub async fn get(
|
pub async fn get(
|
||||||
@@ -173,29 +229,16 @@ pub async fn get(
|
|||||||
format: Option<IoFormat>,
|
format: Option<IoFormat>,
|
||||||
) -> Result<ConfigRes, Error> {
|
) -> Result<ConfigRes, Error> {
|
||||||
let mut db = ctx.db.handle();
|
let mut db = ctx.db.handle();
|
||||||
let pkg_model = crate::db::DatabaseModel::new()
|
let receipts = ConfigGetReceipts::new(&mut db, &id).await?;
|
||||||
.package_data()
|
let action = receipts
|
||||||
.idx_model(&id)
|
.manifest_config
|
||||||
.and_then(|m| m.installed())
|
.get(&mut db)
|
||||||
.expect(&mut db)
|
|
||||||
.await
|
|
||||||
.with_kind(crate::ErrorKind::NotFound)?;
|
|
||||||
let action = pkg_model
|
|
||||||
.clone()
|
|
||||||
.manifest()
|
|
||||||
.config()
|
|
||||||
.get(&mut db, true)
|
|
||||||
.await?
|
.await?
|
||||||
.to_owned()
|
|
||||||
.ok_or_else(|| Error::new(eyre!("{} has no config", id), crate::ErrorKind::NotFound))?;
|
.ok_or_else(|| Error::new(eyre!("{} has no config", id), crate::ErrorKind::NotFound))?;
|
||||||
let version = pkg_model
|
|
||||||
.clone()
|
let volumes = receipts.manifest_volumes.get(&mut db).await?;
|
||||||
.manifest()
|
let version = receipts.manifest_version.get(&mut db).await?;
|
||||||
.version()
|
action.get(&ctx, &id, &version, &volumes).await
|
||||||
.get(&mut db, true)
|
|
||||||
.await?;
|
|
||||||
let volumes = pkg_model.manifest().volumes().get(&mut db, true).await?;
|
|
||||||
action.get(&ctx, &id, &*version, &*volumes).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command(
|
#[command(
|
||||||
@@ -215,6 +258,147 @@ pub fn set(
|
|||||||
Ok((id, config, timeout.map(|d| *d), expire_id))
|
Ok((id, config, timeout.map(|d| *d), expire_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// So, the new locking finds all the possible locks and lifts them up into a bundle of locks.
|
||||||
|
/// Then this bundle will be passed down into the functions that will need to touch the db, and
|
||||||
|
/// instead of doing the locks down in the system, we have already done the locks and can
|
||||||
|
/// do the operation on the db.
|
||||||
|
/// An UnlockedLock has two types, the type of setting and getting from the db, and the second type
|
||||||
|
/// is the keys that we need to insert on getting/setting because we have included wild cards into the paths.
|
||||||
|
pub struct ConfigReceipts {
|
||||||
|
pub dependency_receipt: DependencyReceipt,
|
||||||
|
pub config_receipts: ConfigPointerReceipts,
|
||||||
|
pub update_dependency_receipts: UpdateDependencyReceipts,
|
||||||
|
pub try_heal_receipts: TryHealReceipts,
|
||||||
|
pub break_transitive_receipts: BreakTransitiveReceipts,
|
||||||
|
configured: LockReceipt<bool, String>,
|
||||||
|
config_actions: LockReceipt<ConfigActions, String>,
|
||||||
|
dependencies: LockReceipt<Dependencies, String>,
|
||||||
|
volumes: LockReceipt<crate::volume::Volumes, String>,
|
||||||
|
version: LockReceipt<crate::util::Version, String>,
|
||||||
|
manifest: LockReceipt<Manifest, String>,
|
||||||
|
system_pointers: LockReceipt<Vec<spec::SystemPointerSpec>, String>,
|
||||||
|
pub current_dependents: LockReceipt<BTreeMap<PackageId, CurrentDependencyInfo>, String>,
|
||||||
|
dependency_errors: LockReceipt<DependencyErrors, String>,
|
||||||
|
manifest_dependencies_config: LockReceipt<DependencyConfig, (String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(locks: &mut Vec<LockTargetId>) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
let dependency_receipt = DependencyReceipt::setup(locks);
|
||||||
|
let config_receipts = ConfigPointerReceipts::setup(locks);
|
||||||
|
let update_dependency_receipts = UpdateDependencyReceipts::setup(locks);
|
||||||
|
let break_transitive_receipts = BreakTransitiveReceipts::setup(locks);
|
||||||
|
let try_heal_receipts = TryHealReceipts::setup(locks);
|
||||||
|
|
||||||
|
let configured: LockTarget<bool, String> = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.status().configured())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let config_actions = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.and_then(|x| x.manifest().config())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let dependencies = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest().dependencies())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let volumes = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest().volumes())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let version = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest().version())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let manifest = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let system_pointers = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.system_pointers())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let current_dependents = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.current_dependents())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let dependency_errors = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.status().dependency_errors())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let manifest_dependencies_config = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.and_then(|x| x.manifest().dependencies().star().config())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
dependency_receipt: dependency_receipt(skeleton_key)?,
|
||||||
|
config_receipts: config_receipts(skeleton_key)?,
|
||||||
|
try_heal_receipts: try_heal_receipts(skeleton_key)?,
|
||||||
|
break_transitive_receipts: break_transitive_receipts(skeleton_key)?,
|
||||||
|
update_dependency_receipts: update_dependency_receipts(skeleton_key)?,
|
||||||
|
configured: configured.verify(skeleton_key)?,
|
||||||
|
config_actions: config_actions.verify(skeleton_key)?,
|
||||||
|
dependencies: dependencies.verify(skeleton_key)?,
|
||||||
|
volumes: volumes.verify(skeleton_key)?,
|
||||||
|
version: version.verify(skeleton_key)?,
|
||||||
|
manifest: manifest.verify(skeleton_key)?,
|
||||||
|
system_pointers: system_pointers.verify(skeleton_key)?,
|
||||||
|
current_dependents: current_dependents.verify(skeleton_key)?,
|
||||||
|
dependency_errors: dependency_errors.verify(skeleton_key)?,
|
||||||
|
manifest_dependencies_config: manifest_dependencies_config.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[command(rename = "dry", display(display_serializable))]
|
#[command(rename = "dry", display(display_serializable))]
|
||||||
#[instrument(skip(ctx))]
|
#[instrument(skip(ctx))]
|
||||||
pub async fn set_dry(
|
pub async fn set_dry(
|
||||||
@@ -229,6 +413,7 @@ pub async fn set_dry(
|
|||||||
let mut db = ctx.db.handle();
|
let mut db = ctx.db.handle();
|
||||||
let mut tx = db.begin().await?;
|
let mut tx = db.begin().await?;
|
||||||
let mut breakages = BTreeMap::new();
|
let mut breakages = BTreeMap::new();
|
||||||
|
let locks = ConfigReceipts::new(&mut tx).await?;
|
||||||
configure(
|
configure(
|
||||||
&ctx,
|
&ctx,
|
||||||
&mut tx,
|
&mut tx,
|
||||||
@@ -238,20 +423,11 @@ pub async fn set_dry(
|
|||||||
true,
|
true,
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
&mut breakages,
|
&mut breakages,
|
||||||
|
&locks,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
crate::db::DatabaseModel::new()
|
|
||||||
.package_data()
|
locks.configured.set(&mut tx, true, &id).await?;
|
||||||
.idx_model(&id)
|
|
||||||
.expect(&mut tx)
|
|
||||||
.await?
|
|
||||||
.installed()
|
|
||||||
.expect(&mut tx)
|
|
||||||
.await?
|
|
||||||
.status()
|
|
||||||
.configured()
|
|
||||||
.put(&mut tx, &true)
|
|
||||||
.await?;
|
|
||||||
tx.abort().await?;
|
tx.abort().await?;
|
||||||
Ok(BreakageRes(breakages))
|
Ok(BreakageRes(breakages))
|
||||||
}
|
}
|
||||||
@@ -264,6 +440,7 @@ pub async fn set_impl(
|
|||||||
let mut db = ctx.db.handle();
|
let mut db = ctx.db.handle();
|
||||||
let mut tx = db.begin().await?;
|
let mut tx = db.begin().await?;
|
||||||
let mut breakages = BTreeMap::new();
|
let mut breakages = BTreeMap::new();
|
||||||
|
let locks = ConfigReceipts::new(&mut tx).await?;
|
||||||
configure(
|
configure(
|
||||||
&ctx,
|
&ctx,
|
||||||
&mut tx,
|
&mut tx,
|
||||||
@@ -273,6 +450,7 @@ pub async fn set_impl(
|
|||||||
false,
|
false,
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
&mut breakages,
|
&mut breakages,
|
||||||
|
&locks,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(WithRevision {
|
Ok(WithRevision {
|
||||||
@@ -281,34 +459,27 @@ pub async fn set_impl(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(ctx, db))]
|
#[instrument(skip(ctx, db, receipts))]
|
||||||
pub async fn configure<Db: DbHandle>(
|
pub async fn configure<'a, Db: DbHandle>(
|
||||||
ctx: &RpcContext,
|
ctx: &RpcContext,
|
||||||
db: &mut Db,
|
db: &'a mut Db,
|
||||||
id: &PackageId,
|
id: &PackageId,
|
||||||
config: Option<Config>,
|
config: Option<Config>,
|
||||||
timeout: &Option<Duration>,
|
timeout: &Option<Duration>,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
overrides: &mut BTreeMap<PackageId, Config>,
|
overrides: &mut BTreeMap<PackageId, Config>,
|
||||||
breakages: &mut BTreeMap<PackageId, TaggedDependencyError>,
|
breakages: &mut BTreeMap<PackageId, TaggedDependencyError>,
|
||||||
|
receipts: &ConfigReceipts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
configure_rec(ctx, db, id, config, timeout, dry_run, overrides, breakages).await?;
|
configure_rec(
|
||||||
crate::db::DatabaseModel::new()
|
ctx, db, id, config, timeout, dry_run, overrides, breakages, receipts,
|
||||||
.package_data()
|
)
|
||||||
.idx_model(&id)
|
.await?;
|
||||||
.expect(db)
|
receipts.configured.set(db, true, &id).await?;
|
||||||
.await?
|
|
||||||
.installed()
|
|
||||||
.expect(db)
|
|
||||||
.await?
|
|
||||||
.status()
|
|
||||||
.configured()
|
|
||||||
.put(db, &true)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(ctx, db))]
|
#[instrument(skip(ctx, db, receipts))]
|
||||||
pub fn configure_rec<'a, Db: DbHandle>(
|
pub fn configure_rec<'a, Db: DbHandle>(
|
||||||
ctx: &'a RpcContext,
|
ctx: &'a RpcContext,
|
||||||
db: &'a mut Db,
|
db: &'a mut Db,
|
||||||
@@ -318,48 +489,33 @@ pub fn configure_rec<'a, Db: DbHandle>(
|
|||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
overrides: &'a mut BTreeMap<PackageId, Config>,
|
overrides: &'a mut BTreeMap<PackageId, Config>,
|
||||||
breakages: &'a mut BTreeMap<PackageId, TaggedDependencyError>,
|
breakages: &'a mut BTreeMap<PackageId, TaggedDependencyError>,
|
||||||
|
receipts: &'a ConfigReceipts,
|
||||||
) -> BoxFuture<'a, Result<(), Error>> {
|
) -> BoxFuture<'a, Result<(), Error>> {
|
||||||
async move {
|
async move {
|
||||||
crate::db::DatabaseModel::new()
|
|
||||||
.package_data()
|
|
||||||
.lock(db, LockType::Write)
|
|
||||||
.await?;
|
|
||||||
// fetch data from db
|
// fetch data from db
|
||||||
let pkg_model = crate::db::DatabaseModel::new()
|
let action = receipts
|
||||||
.package_data()
|
.config_actions
|
||||||
.idx_model(id)
|
.get(db, id)
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.expect(db)
|
|
||||||
.await
|
|
||||||
.with_kind(crate::ErrorKind::NotFound)?;
|
|
||||||
let action = pkg_model
|
|
||||||
.clone()
|
|
||||||
.manifest()
|
|
||||||
.config()
|
|
||||||
.get(db, true)
|
|
||||||
.await?
|
.await?
|
||||||
.to_owned()
|
.ok_or_else(not_found)?;
|
||||||
.ok_or_else(|| Error::new(eyre!("{} has no config", id), crate::ErrorKind::NotFound))?;
|
let dependencies = receipts
|
||||||
let version = pkg_model.clone().manifest().version().get(db, true).await?;
|
.dependencies
|
||||||
let dependencies = pkg_model
|
.get(db, id)
|
||||||
.clone()
|
.await?
|
||||||
.manifest()
|
.ok_or_else(not_found)?;
|
||||||
.dependencies()
|
let volumes = receipts.volumes.get(db, id).await?.ok_or_else(not_found)?;
|
||||||
.get(db, true)
|
let is_needs_config = !receipts
|
||||||
.await?;
|
.configured
|
||||||
let volumes = pkg_model.clone().manifest().volumes().get(db, true).await?;
|
.get(db, id)
|
||||||
let is_needs_config = !*pkg_model
|
.await?
|
||||||
.clone()
|
.ok_or_else(not_found)?;
|
||||||
.status()
|
let version = receipts.version.get(db, id).await?.ok_or_else(not_found)?;
|
||||||
.configured()
|
|
||||||
.get(db, true)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// get current config and current spec
|
// get current config and current spec
|
||||||
let ConfigRes {
|
let ConfigRes {
|
||||||
config: old_config,
|
config: old_config,
|
||||||
spec,
|
spec,
|
||||||
} = action.get(ctx, id, &*version, &*volumes).await?;
|
} = action.get(ctx, id, &version, &volumes).await?;
|
||||||
|
|
||||||
// determine new config to use
|
// determine new config to use
|
||||||
let mut config = if let Some(config) = config.or_else(|| old_config.clone()) {
|
let mut config = if let Some(config) = config.or_else(|| old_config.clone()) {
|
||||||
@@ -368,24 +524,26 @@ pub fn configure_rec<'a, Db: DbHandle>(
|
|||||||
spec.gen(&mut rand::rngs::StdRng::from_entropy(), timeout)?
|
spec.gen(&mut rand::rngs::StdRng::from_entropy(), timeout)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let manifest = crate::db::DatabaseModel::new()
|
let manifest = receipts.manifest.get(db, id).await?.ok_or_else(not_found)?;
|
||||||
.package_data()
|
|
||||||
.idx_model(id)
|
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.map::<_, Manifest>(|i| i.manifest())
|
|
||||||
.expect(db)
|
|
||||||
.await?
|
|
||||||
.get(db, true)
|
|
||||||
.await
|
|
||||||
.with_kind(crate::ErrorKind::NotFound)?;
|
|
||||||
|
|
||||||
spec.validate(&*manifest)?;
|
spec.validate(&manifest)?;
|
||||||
spec.matches(&config)?; // check that new config matches spec
|
spec.matches(&config)?; // check that new config matches spec
|
||||||
spec.update(ctx, db, &*manifest, &*overrides, &mut config)
|
spec.update(
|
||||||
.await?; // dereference pointers in the new config
|
ctx,
|
||||||
|
db,
|
||||||
|
&manifest,
|
||||||
|
&*overrides,
|
||||||
|
&mut config,
|
||||||
|
&receipts.config_receipts,
|
||||||
|
)
|
||||||
|
.await?; // dereference pointers in the new config
|
||||||
|
|
||||||
// create backreferences to pointers
|
// create backreferences to pointers
|
||||||
let mut sys = pkg_model.clone().system_pointers().get_mut(db).await?;
|
let mut sys = receipts
|
||||||
|
.system_pointers
|
||||||
|
.get(db, &id)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(not_found)?;
|
||||||
sys.truncate(0);
|
sys.truncate(0);
|
||||||
let mut current_dependencies: BTreeMap<PackageId, CurrentDependencyInfo> = dependencies
|
let mut current_dependencies: BTreeMap<PackageId, CurrentDependencyInfo> = dependencies
|
||||||
.0
|
.0
|
||||||
@@ -418,12 +576,12 @@ pub fn configure_rec<'a, Db: DbHandle>(
|
|||||||
ValueSpecPointer::System(s) => sys.push(s),
|
ValueSpecPointer::System(s) => sys.push(s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sys.save(db).await?;
|
receipts.system_pointers.set(db, sys, &id).await?;
|
||||||
|
|
||||||
let signal = if !dry_run {
|
let signal = if !dry_run {
|
||||||
// run config action
|
// run config action
|
||||||
let res = action
|
let res = action
|
||||||
.set(ctx, id, &*version, &*dependencies, &*volumes, &config)
|
.set(ctx, id, &version, &dependencies, &volumes, &config)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// track dependencies with no pointers
|
// track dependencies with no pointers
|
||||||
@@ -459,50 +617,63 @@ pub fn configure_rec<'a, Db: DbHandle>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// update dependencies
|
// update dependencies
|
||||||
let mut deps = pkg_model.clone().current_dependencies().get_mut(db).await?;
|
remove_from_current_dependents_lists(
|
||||||
remove_from_current_dependents_lists(db, id, deps.keys()).await?; // remove previous
|
db,
|
||||||
add_dependent_to_current_dependents_lists(db, id, ¤t_dependencies).await?; // add new
|
id,
|
||||||
|
current_dependencies.keys(),
|
||||||
|
&receipts.current_dependents,
|
||||||
|
)
|
||||||
|
.await?; // remove previous
|
||||||
|
add_dependent_to_current_dependents_lists(
|
||||||
|
db,
|
||||||
|
id,
|
||||||
|
¤t_dependencies,
|
||||||
|
&receipts.current_dependents,
|
||||||
|
)
|
||||||
|
.await?; // add new
|
||||||
current_dependencies.remove(id);
|
current_dependencies.remove(id);
|
||||||
*deps = current_dependencies.clone();
|
receipts
|
||||||
deps.save(db).await?;
|
.current_dependents
|
||||||
let mut errs = pkg_model
|
.set(db, current_dependencies.clone(), &id)
|
||||||
.clone()
|
|
||||||
.status()
|
|
||||||
.dependency_errors()
|
|
||||||
.get_mut(db)
|
|
||||||
.await?;
|
.await?;
|
||||||
*errs = DependencyErrors::init(ctx, db, &*manifest, ¤t_dependencies).await?;
|
|
||||||
errs.save(db).await?;
|
let errs = receipts
|
||||||
|
.dependency_errors
|
||||||
|
.get(db, &id)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(not_found)?;
|
||||||
|
tracing::warn!("Dependency Errors: {:?}", errs);
|
||||||
|
let errs = DependencyErrors::init(
|
||||||
|
ctx,
|
||||||
|
db,
|
||||||
|
&manifest,
|
||||||
|
¤t_dependencies,
|
||||||
|
&receipts.dependency_receipt.try_heal,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
receipts.dependency_errors.set(db, errs, &id).await?;
|
||||||
|
|
||||||
// cache current config for dependents
|
// cache current config for dependents
|
||||||
overrides.insert(id.clone(), config.clone());
|
overrides.insert(id.clone(), config.clone());
|
||||||
|
|
||||||
// handle dependents
|
// handle dependents
|
||||||
let dependents = pkg_model.clone().current_dependents().get(db, true).await?;
|
let dependents = current_dependencies;
|
||||||
let prev = if is_needs_config { None } else { old_config }
|
let prev = if is_needs_config { None } else { old_config }
|
||||||
.map(Value::Object)
|
.map(Value::Object)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let next = Value::Object(config.clone());
|
let next = Value::Object(config.clone());
|
||||||
for (dependent, dep_info) in dependents.iter().filter(|(dep_id, _)| dep_id != &id) {
|
for (dependent, dep_info) in dependents.iter().filter(|(dep_id, _)| dep_id != &id) {
|
||||||
// check if config passes dependent check
|
// check if config passes dependent check
|
||||||
let dependent_model = crate::db::DatabaseModel::new()
|
if let Some(cfg) = receipts
|
||||||
.package_data()
|
.manifest_dependencies_config
|
||||||
.idx_model(dependent)
|
.get(db, (&dependent, &id))
|
||||||
.and_then(|pkg| pkg.installed())
|
|
||||||
.expect(db)
|
|
||||||
.await?;
|
|
||||||
if let Some(cfg) = &*dependent_model
|
|
||||||
.clone()
|
|
||||||
.manifest()
|
|
||||||
.dependencies()
|
|
||||||
.idx_model(id)
|
|
||||||
.expect(db)
|
|
||||||
.await?
|
|
||||||
.config()
|
|
||||||
.get(db, true)
|
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
let manifest = dependent_model.clone().manifest().get(db, true).await?;
|
let manifest = receipts
|
||||||
|
.manifest
|
||||||
|
.get(db, &dependent)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(not_found)?;
|
||||||
if let Err(error) = cfg
|
if let Err(error) = cfg
|
||||||
.check(
|
.check(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -514,7 +685,15 @@ pub fn configure_rec<'a, Db: DbHandle>(
|
|||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
let dep_err = DependencyError::ConfigUnsatisfied { error };
|
let dep_err = DependencyError::ConfigUnsatisfied { error };
|
||||||
break_transitive(db, dependent, id, dep_err, breakages).await?;
|
break_transitive(
|
||||||
|
db,
|
||||||
|
dependent,
|
||||||
|
id,
|
||||||
|
dep_err,
|
||||||
|
breakages,
|
||||||
|
&receipts.break_transitive_receipts,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle backreferences
|
// handle backreferences
|
||||||
@@ -523,6 +702,7 @@ pub fn configure_rec<'a, Db: DbHandle>(
|
|||||||
if cfg_ptr.select(&next) != cfg_ptr.select(&prev) {
|
if cfg_ptr.select(&next) != cfg_ptr.select(&prev) {
|
||||||
if let Err(e) = configure_rec(
|
if let Err(e) = configure_rec(
|
||||||
ctx, db, dependent, None, timeout, dry_run, overrides, breakages,
|
ctx, db, dependent, None, timeout, dry_run, overrides, breakages,
|
||||||
|
receipts,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@@ -535,6 +715,7 @@ pub fn configure_rec<'a, Db: DbHandle>(
|
|||||||
error: format!("{}", e),
|
error: format!("{}", e),
|
||||||
},
|
},
|
||||||
breakages,
|
breakages,
|
||||||
|
&receipts.break_transitive_receipts,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
@@ -544,7 +725,7 @@ pub fn configure_rec<'a, Db: DbHandle>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
heal_all_dependents_transitive(ctx, db, id).await?;
|
heal_all_dependents_transitive(ctx, db, id, &receipts.dependency_receipt).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,3 +749,67 @@ pub fn configure_rec<'a, Db: DbHandle>(
|
|||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
#[instrument]
|
||||||
|
pub fn not_found() -> Error {
|
||||||
|
Error::new(eyre!("Could not find"), crate::ErrorKind::Incoherent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We want to have a double check that the paths are what we expect them to be.
|
||||||
|
/// Found that earlier the paths where not what we expected them to be.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn ensure_creation_of_config_paths_makes_sense() {
|
||||||
|
let mut fake = patch_db::test_utils::NoOpDb();
|
||||||
|
let config_locks = ConfigReceipts::new(&mut fake).await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.configured.lock.glob),
|
||||||
|
"/package-data/*/installed/status/configured"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.config_actions.lock.glob),
|
||||||
|
"/package-data/*/installed/manifest/config"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.dependencies.lock.glob),
|
||||||
|
"/package-data/*/installed/manifest/dependencies"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.volumes.lock.glob),
|
||||||
|
"/package-data/*/installed/manifest/volumes"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.version.lock.glob),
|
||||||
|
"/package-data/*/installed/manifest/version"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.volumes.lock.glob),
|
||||||
|
"/package-data/*/installed/manifest/volumes"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.manifest.lock.glob),
|
||||||
|
"/package-data/*/installed/manifest"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.manifest.lock.glob),
|
||||||
|
"/package-data/*/installed/manifest"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.system_pointers.lock.glob),
|
||||||
|
"/package-data/*/installed/system-pointers"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.current_dependents.lock.glob),
|
||||||
|
"/package-data/*/installed/current-dependents"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.dependency_errors.lock.glob),
|
||||||
|
"/package-data/*/installed/status/dependency-errors"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.manifest_dependencies_config.lock.glob),
|
||||||
|
"/package-data/*/installed/manifest/dependencies/*/config"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{}", config_locks.system_pointers.lock.glob),
|
||||||
|
"/package-data/*/installed/system-pointers"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use async_trait::async_trait;
|
|||||||
use indexmap::{IndexMap, IndexSet};
|
use indexmap::{IndexMap, IndexSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use jsonpath_lib::Compiled as CompiledJsonPath;
|
use jsonpath_lib::Compiled as CompiledJsonPath;
|
||||||
use patch_db::{DbHandle, OptionModel};
|
use patch_db::{DbHandle, LockReceipt, LockType};
|
||||||
use rand::{CryptoRng, Rng};
|
use rand::{CryptoRng, Rng};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
@@ -44,6 +44,7 @@ pub trait ValueSpec {
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError>;
|
) -> Result<(), ConfigurationError>;
|
||||||
// returns all pointers that are live in the provided config
|
// returns all pointers that are live in the provided config
|
||||||
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath>;
|
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath>;
|
||||||
@@ -160,9 +161,10 @@ where
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
self.inner
|
self.inner
|
||||||
.update(ctx, db, manifest, config_overrides, value)
|
.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
||||||
@@ -204,9 +206,10 @@ where
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
self.inner
|
self.inner
|
||||||
.update(ctx, db, manifest, config_overrides, value)
|
.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
||||||
@@ -281,9 +284,10 @@ where
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
self.inner
|
self.inner
|
||||||
.update(ctx, db, manifest, config_overrides, value)
|
.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
||||||
@@ -343,7 +347,7 @@ pub enum ValueSpecAny {
|
|||||||
Pointer(WithDescription<ValueSpecPointer>),
|
Pointer(WithDescription<ValueSpecPointer>),
|
||||||
}
|
}
|
||||||
impl ValueSpecAny {
|
impl ValueSpecAny {
|
||||||
pub fn name<'a>(&'a self) -> &'a str {
|
pub fn name(&self) -> &'_ str {
|
||||||
match self {
|
match self {
|
||||||
ValueSpecAny::Boolean(b) => b.name.as_str(),
|
ValueSpecAny::Boolean(b) => b.name.as_str(),
|
||||||
ValueSpecAny::Enum(e) => e.name.as_str(),
|
ValueSpecAny::Enum(e) => e.name.as_str(),
|
||||||
@@ -395,16 +399,41 @@ impl ValueSpec for ValueSpecAny {
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
match self {
|
match self {
|
||||||
ValueSpecAny::Boolean(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
ValueSpecAny::Boolean(a) => {
|
||||||
ValueSpecAny::Enum(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
ValueSpecAny::List(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
.await
|
||||||
ValueSpecAny::Number(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
}
|
||||||
ValueSpecAny::Object(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
ValueSpecAny::Enum(a) => {
|
||||||
ValueSpecAny::String(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
ValueSpecAny::Union(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
.await
|
||||||
ValueSpecAny::Pointer(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
}
|
||||||
|
ValueSpecAny::List(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
ValueSpecAny::Number(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
ValueSpecAny::Object(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
ValueSpecAny::String(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
ValueSpecAny::Union(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
ValueSpecAny::Pointer(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
||||||
@@ -489,6 +518,7 @@ impl ValueSpec for ValueSpecBoolean {
|
|||||||
_manifest: &Manifest,
|
_manifest: &Manifest,
|
||||||
_config_overrides: &BTreeMap<PackageId, Config>,
|
_config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
_value: &mut Value,
|
_value: &mut Value,
|
||||||
|
_receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -578,6 +608,7 @@ impl ValueSpec for ValueSpecEnum {
|
|||||||
_manifest: &Manifest,
|
_manifest: &Manifest,
|
||||||
_config_overrides: &BTreeMap<PackageId, Config>,
|
_config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
_value: &mut Value,
|
_value: &mut Value,
|
||||||
|
_receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -664,12 +695,13 @@ where
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
if let Value::Array(ref mut ls) = value {
|
if let Value::Array(ref mut ls) = value {
|
||||||
for (i, val) in ls.into_iter().enumerate() {
|
for (i, val) in ls.into_iter().enumerate() {
|
||||||
match self
|
match self
|
||||||
.spec
|
.spec
|
||||||
.update(ctx, db, manifest, config_overrides, val)
|
.update(ctx, db, manifest, config_overrides, val, receipts)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(ConfigurationError::NoMatch(e)) => {
|
Err(ConfigurationError::NoMatch(e)) => {
|
||||||
@@ -771,13 +803,29 @@ impl ValueSpec for ValueSpecList {
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
match self {
|
match self {
|
||||||
ValueSpecList::Enum(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
ValueSpecList::Enum(a) => {
|
||||||
ValueSpecList::Number(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
ValueSpecList::Object(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
.await
|
||||||
ValueSpecList::String(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
}
|
||||||
ValueSpecList::Union(a) => a.update(ctx, db, manifest, config_overrides, value).await,
|
ValueSpecList::Number(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
ValueSpecList::Object(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
ValueSpecList::String(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
ValueSpecList::Union(a) => {
|
||||||
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
fn pointers(&self, value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
||||||
@@ -898,6 +946,7 @@ impl ValueSpec for ValueSpecNumber {
|
|||||||
_manifest: &Manifest,
|
_manifest: &Manifest,
|
||||||
_config_overrides: &BTreeMap<PackageId, Config>,
|
_config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
_value: &mut Value,
|
_value: &mut Value,
|
||||||
|
_receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -961,10 +1010,11 @@ impl ValueSpec for ValueSpecObject {
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
if let Value::Object(o) = value {
|
if let Value::Object(o) = value {
|
||||||
self.spec
|
self.spec
|
||||||
.update(ctx, db, manifest, config_overrides, o)
|
.update(ctx, db, manifest, config_overrides, o, receipts)
|
||||||
.await
|
.await
|
||||||
} else {
|
} else {
|
||||||
Err(ConfigurationError::NoMatch(NoMatchWithPath::new(
|
Err(ConfigurationError::NoMatch(NoMatchWithPath::new(
|
||||||
@@ -1063,16 +1113,20 @@ impl ConfigSpec {
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
cfg: &mut Config,
|
cfg: &mut Config,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
for (k, vs) in self.0.iter() {
|
for (k, vs) in self.0.iter() {
|
||||||
match cfg.get_mut(k) {
|
match cfg.get_mut(k) {
|
||||||
None => {
|
None => {
|
||||||
let mut v = Value::Null;
|
let mut v = Value::Null;
|
||||||
vs.update(ctx, db, manifest, config_overrides, &mut v)
|
vs.update(ctx, db, manifest, config_overrides, &mut v, receipts)
|
||||||
.await?;
|
.await?;
|
||||||
cfg.insert(k.clone(), v);
|
cfg.insert(k.clone(), v);
|
||||||
}
|
}
|
||||||
Some(v) => match vs.update(ctx, db, manifest, config_overrides, v).await {
|
Some(v) => match vs
|
||||||
|
.update(ctx, db, manifest, config_overrides, v, receipts)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Err(ConfigurationError::NoMatch(e)) => {
|
Err(ConfigurationError::NoMatch(e)) => {
|
||||||
Err(ConfigurationError::NoMatch(e.prepend(k.clone())))
|
Err(ConfigurationError::NoMatch(e.prepend(k.clone())))
|
||||||
}
|
}
|
||||||
@@ -1160,6 +1214,7 @@ impl ValueSpec for ValueSpecString {
|
|||||||
_manifest: &Manifest,
|
_manifest: &Manifest,
|
||||||
_config_overrides: &BTreeMap<PackageId, Config>,
|
_config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
_value: &mut Value,
|
_value: &mut Value,
|
||||||
|
_receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1192,10 +1247,7 @@ impl DefaultableWith for ValueSpecString {
|
|||||||
let candidate = spec.gen(rng);
|
let candidate = spec.gen(rng);
|
||||||
match (spec, &self.pattern) {
|
match (spec, &self.pattern) {
|
||||||
(DefaultString::Entropy(_), Some(pattern))
|
(DefaultString::Entropy(_), Some(pattern))
|
||||||
if !pattern.pattern.is_match(&candidate) =>
|
if !pattern.pattern.is_match(&candidate) => {}
|
||||||
{
|
|
||||||
()
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
return Ok(Value::String(candidate));
|
return Ok(Value::String(candidate));
|
||||||
}
|
}
|
||||||
@@ -1371,6 +1423,7 @@ impl ValueSpec for ValueSpecUnion {
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
if let Value::Object(o) = value {
|
if let Value::Object(o) = value {
|
||||||
match o.get(&self.tag.id) {
|
match o.get(&self.tag.id) {
|
||||||
@@ -1381,7 +1434,10 @@ impl ValueSpec for ValueSpecUnion {
|
|||||||
None => Err(ConfigurationError::NoMatch(NoMatchWithPath::new(
|
None => Err(ConfigurationError::NoMatch(NoMatchWithPath::new(
|
||||||
MatchError::Union(tag.clone(), self.variants.keys().cloned().collect()),
|
MatchError::Union(tag.clone(), self.variants.keys().cloned().collect()),
|
||||||
))),
|
))),
|
||||||
Some(spec) => spec.update(ctx, db, manifest, config_overrides, o).await,
|
Some(spec) => {
|
||||||
|
spec.update(ctx, db, manifest, config_overrides, o, receipts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Some(other) => Err(ConfigurationError::NoMatch(
|
Some(other) => Err(ConfigurationError::NoMatch(
|
||||||
NoMatchWithPath::new(MatchError::InvalidType("string", other.type_of()))
|
NoMatchWithPath::new(MatchError::InvalidType("string", other.type_of()))
|
||||||
@@ -1513,13 +1569,16 @@ impl ValueSpec for ValueSpecPointer {
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
match self {
|
match self {
|
||||||
ValueSpecPointer::Package(a) => {
|
ValueSpecPointer::Package(a) => {
|
||||||
a.update(ctx, db, manifest, config_overrides, value).await
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
ValueSpecPointer::System(a) => {
|
ValueSpecPointer::System(a) => {
|
||||||
a.update(ctx, db, manifest, config_overrides, value).await
|
a.update(ctx, db, manifest, config_overrides, value, receipts)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1563,12 +1622,17 @@ impl PackagePointerSpec {
|
|||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<Value, ConfigurationError> {
|
) -> Result<Value, ConfigurationError> {
|
||||||
match &self {
|
match &self {
|
||||||
PackagePointerSpec::TorKey(key) => key.deref(&manifest.id, &ctx.secret_store).await,
|
PackagePointerSpec::TorKey(key) => key.deref(&manifest.id, &ctx.secret_store).await,
|
||||||
PackagePointerSpec::TorAddress(tor) => tor.deref(db).await,
|
PackagePointerSpec::TorAddress(tor) => {
|
||||||
PackagePointerSpec::LanAddress(lan) => lan.deref(db).await,
|
tor.deref(db, &receipts.interface_addresses_receipt).await
|
||||||
PackagePointerSpec::Config(cfg) => cfg.deref(ctx, db, config_overrides).await,
|
}
|
||||||
|
PackagePointerSpec::LanAddress(lan) => {
|
||||||
|
lan.deref(db, &receipts.interface_addresses_receipt).await
|
||||||
|
}
|
||||||
|
PackagePointerSpec::Config(cfg) => cfg.deref(ctx, db, config_overrides, receipts).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1616,8 +1680,11 @@ impl ValueSpec for PackagePointerSpec {
|
|||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
*value = self.deref(ctx, db, manifest, config_overrides).await?;
|
*value = self
|
||||||
|
.deref(ctx, db, manifest, config_overrides, receipts)
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn pointers(&self, _value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
fn pointers(&self, _value: &Value) -> Result<BTreeSet<ValueSpecPointer>, NoMatchWithPath> {
|
||||||
@@ -1640,16 +1707,17 @@ pub struct TorAddressPointer {
|
|||||||
interface: InterfaceId,
|
interface: InterfaceId,
|
||||||
}
|
}
|
||||||
impl TorAddressPointer {
|
impl TorAddressPointer {
|
||||||
async fn deref<Db: DbHandle>(&self, db: &mut Db) -> Result<Value, ConfigurationError> {
|
async fn deref<Db: DbHandle>(
|
||||||
let addr = crate::db::DatabaseModel::new()
|
&self,
|
||||||
.package_data()
|
db: &mut Db,
|
||||||
.idx_model(&self.package_id)
|
receipt: &InterfaceAddressesReceipt,
|
||||||
.and_then(|pde| pde.installed())
|
) -> Result<Value, ConfigurationError> {
|
||||||
.and_then(|installed| installed.interface_addresses().idx_model(&self.interface))
|
let addr = receipt
|
||||||
.and_then(|addresses| addresses.tor_address())
|
.interface_addresses
|
||||||
.get(db, true)
|
.get(db, (&self.package_id, &self.interface))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigurationError::SystemError(Error::from(e)))?;
|
.map_err(|e| ConfigurationError::SystemError(Error::from(e)))?
|
||||||
|
.and_then(|addresses| addresses.tor_address);
|
||||||
Ok(addr.to_owned().map(Value::String).unwrap_or(Value::Null))
|
Ok(addr.to_owned().map(Value::String).unwrap_or(Value::Null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1664,6 +1732,39 @@ impl fmt::Display for TorAddressPointer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct InterfaceAddressesReceipt {
|
||||||
|
interface_addresses: LockReceipt<crate::db::model::InterfaceAddresses, (String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterfaceAddressesReceipt {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
locks: &mut Vec<patch_db::LockTargetId>,
|
||||||
|
) -> impl FnOnce(&patch_db::Verifier) -> Result<Self, Error> {
|
||||||
|
// let cleanup_receipts = CleanupFailedReceipts::setup(locks);
|
||||||
|
|
||||||
|
let interface_addresses = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.interface_addresses().star())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
// cleanup_receipts: cleanup_receipts(skeleton_key)?,
|
||||||
|
interface_addresses: interface_addresses.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct LanAddressPointer {
|
pub struct LanAddressPointer {
|
||||||
@@ -1672,28 +1773,81 @@ pub struct LanAddressPointer {
|
|||||||
}
|
}
|
||||||
impl fmt::Display for LanAddressPointer {
|
impl fmt::Display for LanAddressPointer {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
let LanAddressPointer {
|
||||||
LanAddressPointer {
|
package_id,
|
||||||
package_id,
|
interface,
|
||||||
interface,
|
} = self;
|
||||||
} => write!(f, "{}: lan-address: {}", package_id, interface),
|
write!(f, "{}: lan-address: {}", package_id, interface)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl LanAddressPointer {
|
impl LanAddressPointer {
|
||||||
async fn deref<Db: DbHandle>(&self, db: &mut Db) -> Result<Value, ConfigurationError> {
|
async fn deref<Db: DbHandle>(
|
||||||
let addr = crate::db::DatabaseModel::new()
|
&self,
|
||||||
.package_data()
|
db: &mut Db,
|
||||||
.idx_model(&self.package_id)
|
receipts: &InterfaceAddressesReceipt,
|
||||||
.and_then(|pde| pde.installed())
|
) -> Result<Value, ConfigurationError> {
|
||||||
.and_then(|installed| installed.interface_addresses().idx_model(&self.interface))
|
let addr = receipts
|
||||||
.and_then(|addresses| addresses.lan_address())
|
.interface_addresses
|
||||||
.get(db, true)
|
.get(db, (&self.package_id, &self.interface))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigurationError::SystemError(Error::from(e)))?;
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.and_then(|x| x.lan_address);
|
||||||
Ok(addr.to_owned().map(Value::String).unwrap_or(Value::Null))
|
Ok(addr.to_owned().map(Value::String).unwrap_or(Value::Null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ConfigPointerReceipts {
|
||||||
|
interface_addresses_receipt: InterfaceAddressesReceipt,
|
||||||
|
manifest_volumes: LockReceipt<crate::volume::Volumes, String>,
|
||||||
|
manifest_version: LockReceipt<crate::util::Version, String>,
|
||||||
|
config_actions: LockReceipt<super::action::ConfigActions, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigPointerReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
locks: &mut Vec<patch_db::LockTargetId>,
|
||||||
|
) -> impl FnOnce(&patch_db::Verifier) -> Result<Self, Error> {
|
||||||
|
let interface_addresses_receipt = InterfaceAddressesReceipt::setup(locks);
|
||||||
|
|
||||||
|
let manifest_volumes = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest().volumes())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let manifest_version = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest().version())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let config_actions = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.and_then(|x| x.manifest().config())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
interface_addresses_receipt: interface_addresses_receipt(skeleton_key)?,
|
||||||
|
manifest_volumes: manifest_volumes.verify(skeleton_key)?,
|
||||||
|
config_actions: config_actions.verify(skeleton_key)?,
|
||||||
|
manifest_version: manifest_version.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct ConfigPointer {
|
pub struct ConfigPointer {
|
||||||
@@ -1710,40 +1864,22 @@ impl ConfigPointer {
|
|||||||
ctx: &RpcContext,
|
ctx: &RpcContext,
|
||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
config_overrides: &BTreeMap<PackageId, Config>,
|
config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<Value, ConfigurationError> {
|
) -> Result<Value, ConfigurationError> {
|
||||||
if let Some(cfg) = config_overrides.get(&self.package_id) {
|
if let Some(cfg) = config_overrides.get(&self.package_id) {
|
||||||
Ok(self.select(&Value::Object(cfg.clone())))
|
Ok(self.select(&Value::Object(cfg.clone())))
|
||||||
} else {
|
} else {
|
||||||
let manifest_model: OptionModel<Manifest> = crate::db::DatabaseModel::new()
|
let id = &self.package_id;
|
||||||
.package_data()
|
let version = receipts.manifest_version.get(db, id).await.ok().flatten();
|
||||||
.idx_model(&self.package_id)
|
let cfg_actions = receipts.config_actions.get(db, id).await.ok().flatten();
|
||||||
.and_then(|pde| pde.installed())
|
let volumes = receipts.manifest_volumes.get(db, id).await.ok().flatten();
|
||||||
.map(|installed| installed.manifest())
|
|
||||||
.into();
|
|
||||||
let version = manifest_model
|
|
||||||
.clone()
|
|
||||||
.map(|manifest| manifest.version())
|
|
||||||
.get(db, true)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ConfigurationError::SystemError(Error::from(e)))?;
|
|
||||||
let cfg_actions = manifest_model
|
|
||||||
.clone()
|
|
||||||
.and_then(|manifest| manifest.config())
|
|
||||||
.get(db, true)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ConfigurationError::SystemError(Error::from(e)))?;
|
|
||||||
let volumes = manifest_model
|
|
||||||
.map(|manifest| manifest.volumes())
|
|
||||||
.get(db, true)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ConfigurationError::SystemError(Error::from(e)))?;
|
|
||||||
if let (Some(version), Some(cfg_actions), Some(volumes)) =
|
if let (Some(version), Some(cfg_actions), Some(volumes)) =
|
||||||
(&*version, &*cfg_actions, &*volumes)
|
(&version, &cfg_actions, &volumes)
|
||||||
{
|
{
|
||||||
let cfg_res = cfg_actions
|
let cfg_res = cfg_actions
|
||||||
.get(&ctx, &self.package_id, version, volumes)
|
.get(ctx, &self.package_id, version, volumes)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigurationError::SystemError(Error::from(e)))?;
|
.map_err(|e| ConfigurationError::SystemError(e))?;
|
||||||
if let Some(cfg) = cfg_res.config {
|
if let Some(cfg) = cfg_res.config {
|
||||||
Ok(self.select(&Value::Object(cfg)))
|
Ok(self.select(&Value::Object(cfg)))
|
||||||
} else {
|
} else {
|
||||||
@@ -1757,13 +1893,12 @@ impl ConfigPointer {
|
|||||||
}
|
}
|
||||||
impl fmt::Display for ConfigPointer {
|
impl fmt::Display for ConfigPointer {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
let ConfigPointer {
|
||||||
ConfigPointer {
|
package_id,
|
||||||
package_id,
|
selector,
|
||||||
selector,
|
..
|
||||||
..
|
} = self;
|
||||||
} => write!(f, "{}: config: {}", package_id, selector),
|
write!(f, "{}: config: {}", package_id, selector)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1909,6 +2044,8 @@ impl ValueSpec for SystemPointerSpec {
|
|||||||
_manifest: &Manifest,
|
_manifest: &Manifest,
|
||||||
_config_overrides: &BTreeMap<PackageId, Config>,
|
_config_overrides: &BTreeMap<PackageId, Config>,
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
|
|
||||||
|
receipts: &ConfigPointerReceipts,
|
||||||
) -> Result<(), ConfigurationError> {
|
) -> Result<(), ConfigurationError> {
|
||||||
*value = self.deref(db).await?;
|
*value = self.deref(db).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ use std::sync::Arc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use color_eyre::eyre::eyre;
|
use patch_db::{json_ptr::JsonPointer, LockReceipt};
|
||||||
use patch_db::json_ptr::JsonPointer;
|
|
||||||
use patch_db::{DbHandle, LockType, PatchDb, Revision};
|
use patch_db::{DbHandle, LockType, PatchDb, Revision};
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use rpc_toolkit::url::Host;
|
use rpc_toolkit::url::Host;
|
||||||
@@ -21,7 +20,6 @@ use tokio::process::Command;
|
|||||||
use tokio::sync::{broadcast, oneshot, Mutex, RwLock};
|
use tokio::sync::{broadcast, oneshot, Mutex, RwLock};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::core::rpc_continuations::{RequestGuid, RpcContinuation};
|
|
||||||
use crate::db::model::{Database, InstalledPackageDataEntry, PackageDataEntry};
|
use crate::db::model::{Database, InstalledPackageDataEntry, PackageDataEntry};
|
||||||
use crate::hostname::{derive_hostname, derive_id, get_product_key};
|
use crate::hostname::{derive_hostname, derive_id, get_product_key};
|
||||||
use crate::install::cleanup::{cleanup_failed, uninstall};
|
use crate::install::cleanup::{cleanup_failed, uninstall};
|
||||||
@@ -36,6 +34,10 @@ use crate::shutdown::Shutdown;
|
|||||||
use crate::status::{MainStatus, Status};
|
use crate::status::{MainStatus, Status};
|
||||||
use crate::util::io::from_yaml_async_reader;
|
use crate::util::io::from_yaml_async_reader;
|
||||||
use crate::util::{AsyncFileExt, Invoke};
|
use crate::util::{AsyncFileExt, Invoke};
|
||||||
|
use crate::{
|
||||||
|
core::rpc_continuations::{RequestGuid, RpcContinuation},
|
||||||
|
install::cleanup::CleanupFailedReceipts,
|
||||||
|
};
|
||||||
use crate::{Error, ResultExt};
|
use crate::{Error, ResultExt};
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Debug, Default, Deserialize)]
|
||||||
@@ -132,6 +134,71 @@ pub struct RpcContextSeed {
|
|||||||
pub wifi_manager: Arc<RwLock<WpaCli>>,
|
pub wifi_manager: Arc<RwLock<WpaCli>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RpcCleanReceipts {
|
||||||
|
cleanup_receipts: CleanupFailedReceipts,
|
||||||
|
packages: LockReceipt<crate::db::model::AllPackageData, ()>,
|
||||||
|
package: LockReceipt<crate::db::model::PackageDataEntry, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RpcCleanReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
locks: &mut Vec<patch_db::LockTargetId>,
|
||||||
|
) -> impl FnOnce(&patch_db::Verifier) -> Result<Self, Error> {
|
||||||
|
let cleanup_receipts = CleanupFailedReceipts::setup(locks);
|
||||||
|
|
||||||
|
let packages = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let package = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
cleanup_receipts: cleanup_receipts(skeleton_key)?,
|
||||||
|
packages: packages.verify(skeleton_key)?,
|
||||||
|
package: package.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RpcSetNginxReceipts {
|
||||||
|
server_info: LockReceipt<crate::db::model::ServerInfo, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RpcSetNginxReceipts {
|
||||||
|
pub async fn new(db: &'_ mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
locks: &mut Vec<patch_db::LockTargetId>,
|
||||||
|
) -> impl FnOnce(&patch_db::Verifier) -> Result<Self, Error> {
|
||||||
|
let server_info = crate::db::DatabaseModel::new()
|
||||||
|
.server_info()
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
server_info: server_info.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RpcContext(Arc<RpcContextSeed>);
|
pub struct RpcContext(Arc<RpcContextSeed>);
|
||||||
impl RpcContext {
|
impl RpcContext {
|
||||||
@@ -203,13 +270,14 @@ impl RpcContext {
|
|||||||
tracing::info!("Initialized Package Managers");
|
tracing::info!("Initialized Package Managers");
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
#[instrument(skip(self, db))]
|
#[instrument(skip(self, db, receipts))]
|
||||||
pub async fn set_nginx_conf<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
|
pub async fn set_nginx_conf<Db: DbHandle>(
|
||||||
|
&self,
|
||||||
|
db: &mut Db,
|
||||||
|
receipts: RpcSetNginxReceipts,
|
||||||
|
) -> Result<(), Error> {
|
||||||
tokio::fs::write("/etc/nginx/sites-available/default", {
|
tokio::fs::write("/etc/nginx/sites-available/default", {
|
||||||
let info = crate::db::DatabaseModel::new()
|
let info = receipts.server_info.get(db).await?;
|
||||||
.server_info()
|
|
||||||
.get(db, true)
|
|
||||||
.await?;
|
|
||||||
format!(
|
format!(
|
||||||
include_str!("../nginx/main-ui.conf.template"),
|
include_str!("../nginx/main-ui.conf.template"),
|
||||||
lan_hostname = info.lan_address.host_str().unwrap(),
|
lan_hostname = info.lan_address.host_str().unwrap(),
|
||||||
@@ -237,34 +305,19 @@ impl RpcContext {
|
|||||||
self.is_closed.store(true, Ordering::SeqCst);
|
self.is_closed.store(true, Ordering::SeqCst);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn cleanup(&self) -> Result<(), Error> {
|
pub async fn cleanup(&self) -> Result<(), Error> {
|
||||||
let mut db = self.db.handle();
|
let mut db = self.db.handle();
|
||||||
crate::db::DatabaseModel::new()
|
let receipts = RpcCleanReceipts::new(&mut db).await?;
|
||||||
.package_data()
|
for (package_id, package) in receipts.packages.get(&mut db).await?.0 {
|
||||||
.lock(&mut db, LockType::Write)
|
|
||||||
.await?;
|
|
||||||
for package_id in crate::db::DatabaseModel::new()
|
|
||||||
.package_data()
|
|
||||||
.keys(&mut db, true)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
if let Err(e) = async {
|
if let Err(e) = async {
|
||||||
let mut pde = crate::db::DatabaseModel::new()
|
match package {
|
||||||
.package_data()
|
|
||||||
.idx_model(&package_id)
|
|
||||||
.get_mut(&mut db)
|
|
||||||
.await?;
|
|
||||||
match pde.as_mut().ok_or_else(|| {
|
|
||||||
Error::new(
|
|
||||||
eyre!("Node does not exist: /package-data/{}", package_id),
|
|
||||||
crate::ErrorKind::Database,
|
|
||||||
)
|
|
||||||
})? {
|
|
||||||
PackageDataEntry::Installing { .. }
|
PackageDataEntry::Installing { .. }
|
||||||
| PackageDataEntry::Restoring { .. }
|
| PackageDataEntry::Restoring { .. }
|
||||||
| PackageDataEntry::Updating { .. } => {
|
| PackageDataEntry::Updating { .. } => {
|
||||||
cleanup_failed(self, &mut db, &package_id).await?;
|
cleanup_failed(self, &mut db, &package_id, &receipts.cleanup_receipts)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
PackageDataEntry::Removing { .. } => {
|
PackageDataEntry::Removing { .. } => {
|
||||||
uninstall(
|
uninstall(
|
||||||
@@ -276,17 +329,12 @@ impl RpcContext {
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
PackageDataEntry::Installed {
|
PackageDataEntry::Installed {
|
||||||
installed:
|
installed,
|
||||||
InstalledPackageDataEntry {
|
static_files,
|
||||||
status: Status { main, .. },
|
manifest,
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
let new_main = match std::mem::replace(
|
let status = installed.status;
|
||||||
main,
|
let main = match status.main {
|
||||||
MainStatus::Stopped, /* placeholder */
|
|
||||||
) {
|
|
||||||
MainStatus::BackingUp { started, .. } => {
|
MainStatus::BackingUp { started, .. } => {
|
||||||
if let Some(_) = started {
|
if let Some(_) = started {
|
||||||
MainStatus::Starting
|
MainStatus::Starting
|
||||||
@@ -295,11 +343,20 @@ impl RpcContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
MainStatus::Running { .. } => MainStatus::Starting,
|
MainStatus::Running { .. } => MainStatus::Starting,
|
||||||
a => a,
|
a => a.clone(),
|
||||||
};
|
};
|
||||||
*main = new_main;
|
let new_package = PackageDataEntry::Installed {
|
||||||
|
installed: InstalledPackageDataEntry {
|
||||||
pde.save(&mut db).await?;
|
status: Status { main, ..status },
|
||||||
|
..installed
|
||||||
|
},
|
||||||
|
static_files,
|
||||||
|
manifest,
|
||||||
|
};
|
||||||
|
receipts
|
||||||
|
.package
|
||||||
|
.set(&mut db, new_package, &package_id)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok::<_, Error>(())
|
Ok::<_, Error>(())
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use patch_db::{DbHandle, LockType};
|
use patch_db::{DbHandle, LockReceipt, LockType};
|
||||||
use rpc_toolkit::command;
|
use rpc_toolkit::command;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::context::RpcContext;
|
|
||||||
use crate::db::util::WithRevision;
|
use crate::db::util::WithRevision;
|
||||||
use crate::dependencies::{
|
use crate::dependencies::{
|
||||||
break_all_dependents_transitive, heal_all_dependents_transitive, BreakageRes, DependencyError,
|
break_all_dependents_transitive, heal_all_dependents_transitive, BreakageRes, DependencyError,
|
||||||
@@ -15,8 +14,53 @@ use crate::s9pk::manifest::PackageId;
|
|||||||
use crate::status::MainStatus;
|
use crate::status::MainStatus;
|
||||||
use crate::util::display_none;
|
use crate::util::display_none;
|
||||||
use crate::util::serde::display_serializable;
|
use crate::util::serde::display_serializable;
|
||||||
|
use crate::{context::RpcContext, dependencies::DependencyReceipt};
|
||||||
use crate::{Error, ResultExt};
|
use crate::{Error, ResultExt};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StartReceipts {
|
||||||
|
dependency_receipt: DependencyReceipt,
|
||||||
|
status: LockReceipt<MainStatus, ()>,
|
||||||
|
version: LockReceipt<crate::util::Version, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StartReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle, id: &PackageId) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks, id);
|
||||||
|
Ok(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 dependency_receipt = DependencyReceipt::setup(locks);
|
||||||
|
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 version = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.map(|x| x.manifest().version())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
dependency_receipt: dependency_receipt(skeleton_key)?,
|
||||||
|
status: status.verify(skeleton_key)?,
|
||||||
|
version: version.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[command(display(display_none))]
|
#[command(display(display_none))]
|
||||||
#[instrument(skip(ctx))]
|
#[instrument(skip(ctx))]
|
||||||
pub async fn start(
|
pub async fn start(
|
||||||
@@ -25,37 +69,13 @@ pub async fn start(
|
|||||||
) -> Result<WithRevision<()>, Error> {
|
) -> Result<WithRevision<()>, Error> {
|
||||||
let mut db = ctx.db.handle();
|
let mut db = ctx.db.handle();
|
||||||
let mut tx = db.begin().await?;
|
let mut tx = db.begin().await?;
|
||||||
crate::db::DatabaseModel::new()
|
let receipts = StartReceipts::new(&mut tx, &id).await?;
|
||||||
.package_data()
|
let version = receipts.version.get(&mut tx).await?;
|
||||||
.lock(&mut tx, LockType::Write)
|
receipts.status.set(&mut tx, MainStatus::Starting).await?;
|
||||||
.await?;
|
heal_all_dependents_transitive(&ctx, &mut tx, &id, &receipts.dependency_receipt).await?;
|
||||||
let installed = crate::db::DatabaseModel::new()
|
|
||||||
.package_data()
|
|
||||||
.idx_model(&id)
|
|
||||||
.and_then(|pkg| pkg.installed())
|
|
||||||
.expect(&mut tx)
|
|
||||||
.await
|
|
||||||
.with_ctx(|_| {
|
|
||||||
(
|
|
||||||
crate::ErrorKind::NotFound,
|
|
||||||
format!("{} is not installed", id),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
installed.lock(&mut tx, LockType::Read).await?;
|
|
||||||
let version = installed
|
|
||||||
.clone()
|
|
||||||
.manifest()
|
|
||||||
.version()
|
|
||||||
.get(&mut tx, true)
|
|
||||||
.await?
|
|
||||||
.to_owned();
|
|
||||||
let mut status = installed.status().main().get_mut(&mut tx).await?;
|
|
||||||
|
|
||||||
*status = MainStatus::Starting;
|
|
||||||
status.save(&mut tx).await?;
|
|
||||||
heal_all_dependents_transitive(&ctx, &mut tx, &id).await?;
|
|
||||||
|
|
||||||
let revision = tx.commit(None).await?;
|
let revision = tx.commit(None).await?;
|
||||||
|
drop(receipts);
|
||||||
|
|
||||||
ctx.managers
|
ctx.managers
|
||||||
.get(&(id, version))
|
.get(&(id, version))
|
||||||
@@ -69,6 +89,40 @@ pub async fn start(
|
|||||||
response: (),
|
response: (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StopReceipts {
|
||||||
|
breaks: crate::dependencies::BreakTransitiveReceipts,
|
||||||
|
status: LockReceipt<MainStatus, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StopReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle, id: &PackageId) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks, id);
|
||||||
|
Ok(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 breaks = crate::dependencies::BreakTransitiveReceipts::setup(locks);
|
||||||
|
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);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
breaks: breaks(skeleton_key)?,
|
||||||
|
status: status.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip(db))]
|
#[instrument(skip(db))]
|
||||||
async fn stop_common<Db: DbHandle>(
|
async fn stop_common<Db: DbHandle>(
|
||||||
@@ -77,27 +131,18 @@ async fn stop_common<Db: DbHandle>(
|
|||||||
breakages: &mut BTreeMap<PackageId, TaggedDependencyError>,
|
breakages: &mut BTreeMap<PackageId, TaggedDependencyError>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut tx = db.begin().await?;
|
let mut tx = db.begin().await?;
|
||||||
let mut status = crate::db::DatabaseModel::new()
|
let receipts = StopReceipts::new(&mut tx, id).await?;
|
||||||
.package_data()
|
receipts.status.set(&mut tx, MainStatus::Stopping).await?;
|
||||||
.idx_model(&id)
|
|
||||||
.and_then(|pkg| pkg.installed())
|
|
||||||
.expect(&mut tx)
|
|
||||||
.await
|
|
||||||
.with_ctx(|_| {
|
|
||||||
(
|
|
||||||
crate::ErrorKind::NotFound,
|
|
||||||
format!("{} is not installed", id),
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
.status()
|
|
||||||
.main()
|
|
||||||
.get_mut(&mut tx)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
*status = MainStatus::Stopping;
|
|
||||||
status.save(&mut tx).await?;
|
|
||||||
tx.save().await?;
|
tx.save().await?;
|
||||||
break_all_dependents_transitive(db, &id, DependencyError::NotRunning, breakages).await?;
|
break_all_dependents_transitive(
|
||||||
|
db,
|
||||||
|
id,
|
||||||
|
DependencyError::NotRunning,
|
||||||
|
breakages,
|
||||||
|
&receipts.breaks,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ pub struct InstalledPackageDataEntry {
|
|||||||
#[model]
|
#[model]
|
||||||
pub manifest: Manifest,
|
pub manifest: Manifest,
|
||||||
pub last_backup: Option<DateTime<Utc>>,
|
pub last_backup: Option<DateTime<Utc>>,
|
||||||
|
#[model]
|
||||||
pub system_pointers: Vec<SystemPointerSpec>,
|
pub system_pointers: Vec<SystemPointerSpec>,
|
||||||
#[model]
|
#[model]
|
||||||
pub dependency_info: BTreeMap<PackageId, StaticDependencyInfo>,
|
pub dependency_info: BTreeMap<PackageId, StaticDependencyInfo>,
|
||||||
|
|||||||
@@ -1,25 +1,75 @@
|
|||||||
use patch_db::DbHandle;
|
use patch_db::{DbHandle, LockReceipt, LockTargetId, LockType, Verifier};
|
||||||
|
|
||||||
use crate::s9pk::manifest::{Manifest, PackageId};
|
use crate::s9pk::manifest::{Manifest, PackageId};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
pub async fn get_packages<Db: DbHandle>(db: &mut Db) -> Result<Vec<PackageId>, Error> {
|
pub struct PackageReceipts {
|
||||||
let packages = crate::db::DatabaseModel::new()
|
package_data: LockReceipt<super::model::AllPackageData, ()>,
|
||||||
.package_data()
|
}
|
||||||
.get(db, false)
|
|
||||||
.await?;
|
impl PackageReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(locks: &mut Vec<LockTargetId>) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
let package_data = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
package_data: package_data.verify(&skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_packages<Db: DbHandle>(
|
||||||
|
db: &mut Db,
|
||||||
|
receipts: &PackageReceipts,
|
||||||
|
) -> Result<Vec<PackageId>, Error> {
|
||||||
|
let packages = receipts.package_data.get(db).await?;
|
||||||
Ok(packages.0.keys().cloned().collect())
|
Ok(packages.0.keys().cloned().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ManifestReceipts {
|
||||||
|
manifest: LockReceipt<Manifest, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ManifestReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle, id: &PackageId) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks, id);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
locks: &mut Vec<LockTargetId>,
|
||||||
|
id: &PackageId,
|
||||||
|
) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
let manifest = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.manifest()
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
manifest: manifest.verify(&skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_manifest<Db: DbHandle>(
|
pub async fn get_manifest<Db: DbHandle>(
|
||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
pkg: &PackageId,
|
pkg: &PackageId,
|
||||||
|
receipts: &ManifestReceipts,
|
||||||
) -> Result<Option<Manifest>, Error> {
|
) -> Result<Option<Manifest>, Error> {
|
||||||
let mpde = crate::db::DatabaseModel::new()
|
Ok(receipts.manifest.get(db, pkg).await?)
|
||||||
.package_data()
|
|
||||||
.idx_model(pkg)
|
|
||||||
.get(db, false)
|
|
||||||
.await?
|
|
||||||
.into_owned();
|
|
||||||
Ok(mpde.map(|pde| pde.manifest()))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,20 @@ use color_eyre::eyre::eyre;
|
|||||||
use emver::VersionRange;
|
use emver::VersionRange;
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use patch_db::{DbHandle, HasModel, LockType, Map, MapModel, PatchDbHandle};
|
use patch_db::{
|
||||||
|
DbHandle, HasModel, LockReceipt, LockTargetId, LockType, Map, MapModel, PatchDbHandle, Verifier,
|
||||||
|
};
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use rpc_toolkit::command;
|
use rpc_toolkit::command;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::action::{ActionImplementation, NoOutput};
|
use crate::action::{ActionImplementation, NoOutput};
|
||||||
use crate::config::action::ConfigRes;
|
|
||||||
use crate::config::spec::PackagePointerSpec;
|
use crate::config::spec::PackagePointerSpec;
|
||||||
use crate::config::{Config, ConfigSpec};
|
use crate::config::{action::ConfigActions, Config, ConfigSpec};
|
||||||
|
use crate::config::{action::ConfigRes, not_found, ConfigReceipts};
|
||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
use crate::db::model::{CurrentDependencyInfo, InstalledPackageDataEntry};
|
use crate::db::model::{CurrentDependencyInfo, InstalledPackageDataEntry};
|
||||||
use crate::error::ResultExt;
|
|
||||||
use crate::s9pk::manifest::{Manifest, PackageId};
|
use crate::s9pk::manifest::{Manifest, PackageId};
|
||||||
use crate::status::health_check::{HealthCheckId, HealthCheckResult};
|
use crate::status::health_check::{HealthCheckId, HealthCheckResult};
|
||||||
use crate::status::{MainStatus, Status};
|
use crate::status::{MainStatus, Status};
|
||||||
@@ -55,6 +56,72 @@ pub enum DependencyError {
|
|||||||
Transitive, // { "type": "transitive" }
|
Transitive, // { "type": "transitive" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TryHealReceipts {
|
||||||
|
status: LockReceipt<Status, String>,
|
||||||
|
manifest: LockReceipt<Manifest, String>,
|
||||||
|
manifest_version: LockReceipt<Version, String>,
|
||||||
|
current_dependencies: LockReceipt<BTreeMap<PackageId, CurrentDependencyInfo>, String>,
|
||||||
|
dependency_errors: LockReceipt<DependencyErrors, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryHealReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(locks: &mut Vec<LockTargetId>) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
let manifest_version = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest().version())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let status = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.status())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let manifest = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let current_dependencies = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.current_dependencies())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let dependency_errors = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.status().dependency_errors())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
status: status.verify(skeleton_key)?,
|
||||||
|
manifest_version: manifest_version.verify(skeleton_key)?,
|
||||||
|
current_dependencies: current_dependencies.verify(skeleton_key)?,
|
||||||
|
manifest: manifest.verify(skeleton_key)?,
|
||||||
|
dependency_errors: dependency_errors.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DependencyError {
|
impl DependencyError {
|
||||||
pub fn cmp_priority(&self, other: &DependencyError) -> std::cmp::Ordering {
|
pub fn cmp_priority(&self, other: &DependencyError) -> std::cmp::Ordering {
|
||||||
use std::cmp::Ordering::*;
|
use std::cmp::Ordering::*;
|
||||||
@@ -114,7 +181,7 @@ impl DependencyError {
|
|||||||
(DependencyError::Transitive, _) => DependencyError::Transitive,
|
(DependencyError::Transitive, _) => DependencyError::Transitive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[instrument(skip(ctx, db))]
|
#[instrument(skip(ctx, db, receipts))]
|
||||||
pub fn try_heal<'a, Db: DbHandle>(
|
pub fn try_heal<'a, Db: DbHandle>(
|
||||||
self,
|
self,
|
||||||
ctx: &'a RpcContext,
|
ctx: &'a RpcContext,
|
||||||
@@ -123,42 +190,33 @@ impl DependencyError {
|
|||||||
dependency: &'a PackageId,
|
dependency: &'a PackageId,
|
||||||
mut dependency_config: Option<Config>,
|
mut dependency_config: Option<Config>,
|
||||||
info: &'a DepInfo,
|
info: &'a DepInfo,
|
||||||
|
receipts: &'a TryHealReceipts,
|
||||||
) -> BoxFuture<'a, Result<Option<Self>, Error>> {
|
) -> BoxFuture<'a, Result<Option<Self>, Error>> {
|
||||||
async move {
|
async move {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
DependencyError::NotInstalled => {
|
DependencyError::NotInstalled => {
|
||||||
if crate::db::DatabaseModel::new()
|
if receipts.status.get(db, dependency).await?.is_some() {
|
||||||
.package_data()
|
|
||||||
.idx_model(dependency)
|
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.exists(db, true)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
DependencyError::IncorrectVersion {
|
DependencyError::IncorrectVersion {
|
||||||
expected: info.version.clone(),
|
expected: info.version.clone(),
|
||||||
received: Default::default(),
|
received: Default::default(),
|
||||||
}
|
}
|
||||||
.try_heal(ctx, db, id, dependency, dependency_config, info)
|
.try_heal(ctx, db, id, dependency, dependency_config, info, receipts)
|
||||||
.await?
|
.await?
|
||||||
} else {
|
} else {
|
||||||
Some(DependencyError::NotInstalled)
|
Some(DependencyError::NotInstalled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DependencyError::IncorrectVersion { expected, .. } => {
|
DependencyError::IncorrectVersion { expected, .. } => {
|
||||||
let version: Version = crate::db::DatabaseModel::new()
|
let version: Version = receipts
|
||||||
.package_data()
|
.manifest_version
|
||||||
.idx_model(dependency)
|
.get(db, dependency)
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.map(|m| m.manifest().version())
|
|
||||||
.get(db, true)
|
|
||||||
.await?
|
.await?
|
||||||
.into_owned()
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if version.satisfies(&expected) {
|
if version.satisfies(&expected) {
|
||||||
DependencyError::ConfigUnsatisfied {
|
DependencyError::ConfigUnsatisfied {
|
||||||
error: String::new(),
|
error: String::new(),
|
||||||
}
|
}
|
||||||
.try_heal(ctx, db, id, dependency, dependency_config, info)
|
.try_heal(ctx, db, id, dependency, dependency_config, info, receipts)
|
||||||
.await?
|
.await?
|
||||||
} else {
|
} else {
|
||||||
Some(DependencyError::IncorrectVersion {
|
Some(DependencyError::IncorrectVersion {
|
||||||
@@ -168,24 +226,14 @@ impl DependencyError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DependencyError::ConfigUnsatisfied { .. } => {
|
DependencyError::ConfigUnsatisfied { .. } => {
|
||||||
let dependent_manifest = crate::db::DatabaseModel::new()
|
let dependent_manifest =
|
||||||
.package_data()
|
receipts.manifest.get(db, id).await?.ok_or_else(not_found)?;
|
||||||
.idx_model(id)
|
let dependency_manifest = receipts
|
||||||
.and_then(|m| m.installed())
|
.manifest
|
||||||
.map::<_, Manifest>(|m| m.manifest())
|
.get(db, dependency)
|
||||||
.expect(db)
|
|
||||||
.await?
|
.await?
|
||||||
.get(db, true)
|
.ok_or_else(not_found)?;
|
||||||
.await?;
|
|
||||||
let dependency_manifest = crate::db::DatabaseModel::new()
|
|
||||||
.package_data()
|
|
||||||
.idx_model(dependency)
|
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.map::<_, Manifest>(|m| m.manifest())
|
|
||||||
.expect(db)
|
|
||||||
.await?
|
|
||||||
.get(db, true)
|
|
||||||
.await?;
|
|
||||||
let dependency_config = if let Some(cfg) = dependency_config.take() {
|
let dependency_config = if let Some(cfg) = dependency_config.take() {
|
||||||
cfg
|
cfg
|
||||||
} else if let Some(cfg_info) = &dependency_manifest.config {
|
} else if let Some(cfg_info) = &dependency_manifest.config {
|
||||||
@@ -217,40 +265,39 @@ impl DependencyError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DependencyError::NotRunning
|
DependencyError::NotRunning
|
||||||
.try_heal(ctx, db, id, dependency, Some(dependency_config), info)
|
.try_heal(
|
||||||
|
ctx,
|
||||||
|
db,
|
||||||
|
id,
|
||||||
|
dependency,
|
||||||
|
Some(dependency_config),
|
||||||
|
info,
|
||||||
|
receipts,
|
||||||
|
)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
DependencyError::NotRunning => {
|
DependencyError::NotRunning => {
|
||||||
let status = crate::db::DatabaseModel::new()
|
let status = receipts
|
||||||
.package_data()
|
.status
|
||||||
.idx_model(dependency)
|
.get(db, dependency)
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.map::<_, Status>(|m| m.status())
|
|
||||||
.expect(db)
|
|
||||||
.await?
|
.await?
|
||||||
.get(db, true)
|
.ok_or_else(not_found)?;
|
||||||
.await?;
|
|
||||||
if status.main.running() {
|
if status.main.running() {
|
||||||
DependencyError::HealthChecksFailed {
|
DependencyError::HealthChecksFailed {
|
||||||
failures: BTreeMap::new(),
|
failures: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
.try_heal(ctx, db, id, dependency, dependency_config, info)
|
.try_heal(ctx, db, id, dependency, dependency_config, info, receipts)
|
||||||
.await?
|
.await?
|
||||||
} else {
|
} else {
|
||||||
Some(DependencyError::NotRunning)
|
Some(DependencyError::NotRunning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DependencyError::HealthChecksFailed { .. } => {
|
DependencyError::HealthChecksFailed { .. } => {
|
||||||
let status = crate::db::DatabaseModel::new()
|
let status = receipts
|
||||||
.package_data()
|
.status
|
||||||
.idx_model(dependency)
|
.get(db, dependency)
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.map::<_, Status>(|m| m.status())
|
|
||||||
.expect(db)
|
|
||||||
.await?
|
.await?
|
||||||
.get(db, true)
|
.ok_or_else(not_found)?;
|
||||||
.await?
|
|
||||||
.into_owned();
|
|
||||||
match status.main {
|
match status.main {
|
||||||
MainStatus::BackingUp {
|
MainStatus::BackingUp {
|
||||||
started: Some(_),
|
started: Some(_),
|
||||||
@@ -260,19 +307,14 @@ impl DependencyError {
|
|||||||
let mut failures = BTreeMap::new();
|
let mut failures = BTreeMap::new();
|
||||||
for (check, res) in health {
|
for (check, res) in health {
|
||||||
if !matches!(res, HealthCheckResult::Success)
|
if !matches!(res, HealthCheckResult::Success)
|
||||||
&& crate::db::DatabaseModel::new()
|
&& receipts
|
||||||
.package_data()
|
.current_dependencies
|
||||||
.idx_model(id)
|
.get(db, id)
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.and_then::<_, CurrentDependencyInfo>(|m| {
|
|
||||||
m.current_dependencies().idx_model(dependency)
|
|
||||||
})
|
|
||||||
.get(db, true)
|
|
||||||
.await?
|
.await?
|
||||||
.into_owned()
|
.ok_or_else(not_found)?
|
||||||
.map(|i| i.health_checks)
|
.get(dependency)
|
||||||
.unwrap_or_default()
|
.map(|x| x.health_checks.contains(&check))
|
||||||
.contains(&check)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
failures.insert(check.clone(), res.clone());
|
failures.insert(check.clone(), res.clone());
|
||||||
}
|
}
|
||||||
@@ -281,27 +323,39 @@ impl DependencyError {
|
|||||||
Some(DependencyError::HealthChecksFailed { failures })
|
Some(DependencyError::HealthChecksFailed { failures })
|
||||||
} else {
|
} else {
|
||||||
DependencyError::Transitive
|
DependencyError::Transitive
|
||||||
.try_heal(ctx, db, id, dependency, dependency_config, info)
|
.try_heal(
|
||||||
|
ctx,
|
||||||
|
db,
|
||||||
|
id,
|
||||||
|
dependency,
|
||||||
|
dependency_config,
|
||||||
|
info,
|
||||||
|
receipts,
|
||||||
|
)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MainStatus::Starting => {
|
MainStatus::Starting => {
|
||||||
DependencyError::Transitive
|
DependencyError::Transitive
|
||||||
.try_heal(ctx, db, id, dependency, dependency_config, info)
|
.try_heal(
|
||||||
|
ctx,
|
||||||
|
db,
|
||||||
|
id,
|
||||||
|
dependency,
|
||||||
|
dependency_config,
|
||||||
|
info,
|
||||||
|
receipts,
|
||||||
|
)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
_ => return Ok(Some(DependencyError::NotRunning)),
|
_ => return Ok(Some(DependencyError::NotRunning)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DependencyError::Transitive => {
|
DependencyError::Transitive => {
|
||||||
if crate::db::DatabaseModel::new()
|
if receipts
|
||||||
.package_data()
|
.dependency_errors
|
||||||
.idx_model(dependency)
|
.get(db, dependency)
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.map::<_, DependencyErrors>(|m| m.status().dependency_errors())
|
|
||||||
.get(db, true)
|
|
||||||
.await?
|
.await?
|
||||||
.into_owned()
|
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.0
|
.0
|
||||||
.is_empty()
|
.is_empty()
|
||||||
@@ -406,6 +460,7 @@ impl DepInfo {
|
|||||||
dependency_id: &PackageId,
|
dependency_id: &PackageId,
|
||||||
dependency_config: Option<Config>, // fetch if none
|
dependency_config: Option<Config>, // fetch if none
|
||||||
dependent_id: &PackageId,
|
dependent_id: &PackageId,
|
||||||
|
receipts: &TryHealReceipts,
|
||||||
) -> Result<Result<(), DependencyError>, Error> {
|
) -> Result<Result<(), DependencyError>, Error> {
|
||||||
Ok(
|
Ok(
|
||||||
if let Some(err) = DependencyError::NotInstalled
|
if let Some(err) = DependencyError::NotInstalled
|
||||||
@@ -416,6 +471,7 @@ impl DepInfo {
|
|||||||
dependency_id,
|
dependency_id,
|
||||||
dependency_config,
|
dependency_config,
|
||||||
self,
|
self,
|
||||||
|
receipts,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
@@ -477,6 +533,78 @@ impl DependencyConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DependencyConfigReceipts {
|
||||||
|
dependencies: LockReceipt<Dependencies, ()>,
|
||||||
|
dependency_volumes: LockReceipt<Volumes, ()>,
|
||||||
|
dependency_version: LockReceipt<Version, ()>,
|
||||||
|
dependency_config_action: LockReceipt<ConfigActions, ()>,
|
||||||
|
package_volumes: LockReceipt<Volumes, ()>,
|
||||||
|
package_version: LockReceipt<Version, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DependencyConfigReceipts {
|
||||||
|
pub async fn new(
|
||||||
|
db: &'_ mut impl DbHandle,
|
||||||
|
package_id: &PackageId,
|
||||||
|
dependency_id: &PackageId,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let dependencies = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(package_id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.map(|x| x.manifest().dependencies())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
let dependency_volumes = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(dependency_id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.map(|x| x.manifest().volumes())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
let dependency_version = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(dependency_id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.map(|x| x.manifest().version())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
let dependency_config_action = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(dependency_id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.and_then(|x| x.manifest().config())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
let package_volumes = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(package_id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.map(|x| x.manifest().volumes())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
let package_version = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(package_id)
|
||||||
|
.and_then(|x| x.installed())
|
||||||
|
.map(|x| x.manifest().version())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
|
||||||
|
let skeleton_key = db.lock_all(locks).await?;
|
||||||
|
Ok(Self {
|
||||||
|
dependencies: dependencies.verify(&skeleton_key)?,
|
||||||
|
dependency_volumes: dependency_volumes.verify(&skeleton_key)?,
|
||||||
|
dependency_version: dependency_version.verify(&skeleton_key)?,
|
||||||
|
dependency_config_action: dependency_config_action.verify(&skeleton_key)?,
|
||||||
|
package_volumes: package_volumes.verify(&skeleton_key)?,
|
||||||
|
package_version: package_version.verify(&skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[command(
|
#[command(
|
||||||
subcommands(self(configure_impl(async)), configure_dry),
|
subcommands(self(configure_impl(async)), configure_dry),
|
||||||
display(display_none)
|
display(display_none)
|
||||||
@@ -493,11 +621,14 @@ pub async fn configure_impl(
|
|||||||
(pkg_id, dep_id): (PackageId, PackageId),
|
(pkg_id, dep_id): (PackageId, PackageId),
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut db = ctx.db.handle();
|
let mut db = ctx.db.handle();
|
||||||
|
let receipts = DependencyConfigReceipts::new(&mut db, &pkg_id, &dep_id).await?;
|
||||||
let ConfigDryRes {
|
let ConfigDryRes {
|
||||||
old_config: _,
|
old_config: _,
|
||||||
new_config,
|
new_config,
|
||||||
spec: _,
|
spec: _,
|
||||||
} = configure_logic(ctx.clone(), &mut db, (pkg_id, dep_id.clone())).await?;
|
} = configure_logic(ctx.clone(), &mut db, (pkg_id, dep_id.clone()), &receipts).await?;
|
||||||
|
|
||||||
|
let locks = ConfigReceipts::new(&mut db).await?;
|
||||||
Ok(crate::config::configure(
|
Ok(crate::config::configure(
|
||||||
&ctx,
|
&ctx,
|
||||||
&mut db,
|
&mut db,
|
||||||
@@ -507,6 +638,7 @@ pub async fn configure_impl(
|
|||||||
false,
|
false,
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
|
&locks,
|
||||||
)
|
)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
@@ -526,67 +658,25 @@ pub async fn configure_dry(
|
|||||||
#[parent_data] (pkg_id, dependency_id): (PackageId, PackageId),
|
#[parent_data] (pkg_id, dependency_id): (PackageId, PackageId),
|
||||||
) -> Result<ConfigDryRes, Error> {
|
) -> Result<ConfigDryRes, Error> {
|
||||||
let mut db = ctx.db.handle();
|
let mut db = ctx.db.handle();
|
||||||
configure_logic(ctx, &mut db, (pkg_id, dependency_id)).await
|
let receipts = DependencyConfigReceipts::new(&mut db, &pkg_id, &dependency_id).await?;
|
||||||
|
configure_logic(ctx, &mut db, (pkg_id, dependency_id), &receipts).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn configure_logic(
|
pub async fn configure_logic(
|
||||||
ctx: RpcContext,
|
ctx: RpcContext,
|
||||||
db: &mut PatchDbHandle,
|
db: &mut PatchDbHandle,
|
||||||
(pkg_id, dependency_id): (PackageId, PackageId),
|
(pkg_id, dependency_id): (PackageId, PackageId),
|
||||||
|
receipts: &DependencyConfigReceipts,
|
||||||
) -> Result<ConfigDryRes, Error> {
|
) -> Result<ConfigDryRes, Error> {
|
||||||
crate::db::DatabaseModel::new()
|
let pkg_version = receipts.package_version.get(db).await?;
|
||||||
.package_data()
|
let pkg_volumes = receipts.package_volumes.get(db).await?;
|
||||||
.lock(db, LockType::Read)
|
let dependency_config_action = receipts.dependency_config_action.get(db).await?;
|
||||||
.await?;
|
let dependency_version = receipts.dependency_version.get(db).await?;
|
||||||
let pkg_model = crate::db::DatabaseModel::new()
|
let dependency_volumes = receipts.dependency_volumes.get(db).await?;
|
||||||
.package_data()
|
let dependencies = receipts.dependencies.get(db).await?;
|
||||||
.idx_model(&pkg_id)
|
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.expect(db)
|
|
||||||
.await
|
|
||||||
.with_kind(crate::ErrorKind::NotFound)?;
|
|
||||||
let pkg_version = pkg_model.clone().manifest().version().get(db, true).await?;
|
|
||||||
let pkg_volumes = pkg_model.clone().manifest().volumes().get(db, true).await?;
|
|
||||||
let dependency_model = crate::db::DatabaseModel::new()
|
|
||||||
.package_data()
|
|
||||||
.idx_model(&dependency_id)
|
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.expect(db)
|
|
||||||
.await
|
|
||||||
.with_kind(crate::ErrorKind::NotFound)?;
|
|
||||||
let dependency_config_action = dependency_model
|
|
||||||
.clone()
|
|
||||||
.manifest()
|
|
||||||
.config()
|
|
||||||
.get(db, true)
|
|
||||||
.await?
|
|
||||||
.to_owned()
|
|
||||||
.ok_or_else(|| {
|
|
||||||
Error::new(
|
|
||||||
eyre!("{} has no config", dependency_id),
|
|
||||||
crate::ErrorKind::NotFound,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let dependency_version = dependency_model
|
|
||||||
.clone()
|
|
||||||
.manifest()
|
|
||||||
.version()
|
|
||||||
.get(db, true)
|
|
||||||
.await?;
|
|
||||||
let dependency_volumes = dependency_model
|
|
||||||
.clone()
|
|
||||||
.manifest()
|
|
||||||
.volumes()
|
|
||||||
.get(db, true)
|
|
||||||
.await?;
|
|
||||||
let dependencies = pkg_model
|
|
||||||
.clone()
|
|
||||||
.manifest()
|
|
||||||
.dependencies()
|
|
||||||
.get(db, true)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let dependency = dependencies
|
let dependency = dependencies
|
||||||
|
.0
|
||||||
.get(&dependency_id)
|
.get(&dependency_id)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
Error::new(
|
Error::new(
|
||||||
@@ -617,8 +707,8 @@ pub async fn configure_logic(
|
|||||||
.get(
|
.get(
|
||||||
&ctx,
|
&ctx,
|
||||||
&dependency_id,
|
&dependency_id,
|
||||||
&*dependency_version,
|
&dependency_version,
|
||||||
&*dependency_volumes,
|
&dependency_volumes,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -650,8 +740,7 @@ pub async fn configure_logic(
|
|||||||
spec,
|
spec,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
#[instrument(skip(db, current_dependencies, current_dependent_receipt))]
|
||||||
#[instrument(skip(db, current_dependencies))]
|
|
||||||
pub async fn add_dependent_to_current_dependents_lists<
|
pub async fn add_dependent_to_current_dependents_lists<
|
||||||
'a,
|
'a,
|
||||||
Db: DbHandle,
|
Db: DbHandle,
|
||||||
@@ -660,19 +749,15 @@ pub async fn add_dependent_to_current_dependents_lists<
|
|||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
dependent_id: &PackageId,
|
dependent_id: &PackageId,
|
||||||
current_dependencies: I,
|
current_dependencies: I,
|
||||||
|
current_dependent_receipt: &LockReceipt<BTreeMap<PackageId, CurrentDependencyInfo>, String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
for (dependency, dep_info) in current_dependencies {
|
for (dependency, dep_info) in current_dependencies {
|
||||||
if let Some(dependency_model) = crate::db::DatabaseModel::new()
|
if let Some(mut dependency_dependents) =
|
||||||
.package_data()
|
current_dependent_receipt.get(db, dependency).await?
|
||||||
.idx_model(&dependency)
|
|
||||||
.and_then(|pkg| pkg.installed())
|
|
||||||
.check(db)
|
|
||||||
.await?
|
|
||||||
{
|
{
|
||||||
dependency_model
|
dependency_dependents.insert(dependent_id.clone(), dep_info.clone());
|
||||||
.current_dependents()
|
current_dependent_receipt
|
||||||
.idx_model(dependent_id)
|
.set(db, dependency_dependents, dependency)
|
||||||
.put(db, &dep_info)
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -697,6 +782,7 @@ impl DependencyErrors {
|
|||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
current_dependencies: &BTreeMap<PackageId, CurrentDependencyInfo>,
|
current_dependencies: &BTreeMap<PackageId, CurrentDependencyInfo>,
|
||||||
|
receipts: &TryHealReceipts,
|
||||||
) -> Result<DependencyErrors, Error> {
|
) -> Result<DependencyErrors, Error> {
|
||||||
let mut res = BTreeMap::new();
|
let mut res = BTreeMap::new();
|
||||||
for (dependency_id, info) in current_dependencies.keys().filter_map(|dependency_id| {
|
for (dependency_id, info) in current_dependencies.keys().filter_map(|dependency_id| {
|
||||||
@@ -707,7 +793,7 @@ impl DependencyErrors {
|
|||||||
.map(|info| (dependency_id, info))
|
.map(|info| (dependency_id, info))
|
||||||
}) {
|
}) {
|
||||||
if let Err(e) = info
|
if let Err(e) = info
|
||||||
.satisfied(ctx, db, dependency_id, None, &manifest.id)
|
.satisfied(ctx, db, dependency_id, None, &manifest.id, receipts)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
res.insert(dependency_id.clone(), e);
|
res.insert(dependency_id.clone(), e);
|
||||||
@@ -735,49 +821,86 @@ pub async fn break_all_dependents_transitive<'a, Db: DbHandle>(
|
|||||||
id: &'a PackageId,
|
id: &'a PackageId,
|
||||||
error: DependencyError,
|
error: DependencyError,
|
||||||
breakages: &'a mut BTreeMap<PackageId, TaggedDependencyError>,
|
breakages: &'a mut BTreeMap<PackageId, TaggedDependencyError>,
|
||||||
|
receipts: &'a BreakTransitiveReceipts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
for dependent in crate::db::DatabaseModel::new()
|
for dependent in receipts
|
||||||
.package_data()
|
.current_dependents
|
||||||
.idx_model(id)
|
.get(db, id)
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.expect(db)
|
|
||||||
.await?
|
.await?
|
||||||
.current_dependents()
|
.iter()
|
||||||
.keys(db, true)
|
.flat_map(|x| x.keys())
|
||||||
.await?
|
.filter(|dependent| id != *dependent)
|
||||||
.into_iter()
|
|
||||||
.filter(|dependent| id != dependent)
|
|
||||||
{
|
{
|
||||||
break_transitive(db, &dependent, id, error.clone(), breakages).await?;
|
break_transitive(db, dependent, id, error.clone(), breakages, receipts).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(db))]
|
#[derive(Clone)]
|
||||||
|
pub struct BreakTransitiveReceipts {
|
||||||
|
pub dependency_receipt: DependencyReceipt,
|
||||||
|
dependency_errors: LockReceipt<DependencyErrors, String>,
|
||||||
|
current_dependents: LockReceipt<BTreeMap<PackageId, CurrentDependencyInfo>, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BreakTransitiveReceipts {
|
||||||
|
pub async fn new(db: &'_ mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(locks: &mut Vec<LockTargetId>) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
let dependency_receipt = DependencyReceipt::setup(locks);
|
||||||
|
let dependency_errors = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.status().dependency_errors())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let current_dependents = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.current_dependents())
|
||||||
|
.make_locker(LockType::Exist)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
dependency_receipt: dependency_receipt(skeleton_key)?,
|
||||||
|
dependency_errors: dependency_errors.verify(skeleton_key)?,
|
||||||
|
current_dependents: current_dependents.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(db, receipts))]
|
||||||
pub fn break_transitive<'a, Db: DbHandle>(
|
pub fn break_transitive<'a, Db: DbHandle>(
|
||||||
db: &'a mut Db,
|
db: &'a mut Db,
|
||||||
id: &'a PackageId,
|
id: &'a PackageId,
|
||||||
dependency: &'a PackageId,
|
dependency: &'a PackageId,
|
||||||
error: DependencyError,
|
error: DependencyError,
|
||||||
breakages: &'a mut BTreeMap<PackageId, TaggedDependencyError>,
|
breakages: &'a mut BTreeMap<PackageId, TaggedDependencyError>,
|
||||||
|
receipts: &'a BreakTransitiveReceipts,
|
||||||
) -> BoxFuture<'a, Result<(), Error>> {
|
) -> BoxFuture<'a, Result<(), Error>> {
|
||||||
async move {
|
async move {
|
||||||
let mut tx = db.begin().await?;
|
let mut tx = db.begin().await?;
|
||||||
let model = crate::db::DatabaseModel::new()
|
let mut dependency_errors = receipts
|
||||||
.package_data()
|
.dependency_errors
|
||||||
.idx_model(id)
|
.get(&mut tx, id)
|
||||||
.and_then(|m| m.installed())
|
.await?
|
||||||
.expect(&mut tx)
|
.ok_or_else(not_found)?;
|
||||||
.await?;
|
|
||||||
let mut status = model.clone().status().get_mut(&mut tx).await?;
|
|
||||||
|
|
||||||
let old = status.dependency_errors.0.remove(dependency);
|
let old = dependency_errors.0.remove(dependency);
|
||||||
let newly_broken = if let Some(e) = &old {
|
let newly_broken = if let Some(e) = &old {
|
||||||
error.cmp_priority(&e) == Ordering::Greater
|
error.cmp_priority(&e) == Ordering::Greater
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
status.dependency_errors.0.insert(
|
dependency_errors.0.insert(
|
||||||
dependency.clone(),
|
dependency.clone(),
|
||||||
if let Some(old) = old {
|
if let Some(old) = old {
|
||||||
old.merge_with(error.clone())
|
old.merge_with(error.clone())
|
||||||
@@ -793,12 +916,25 @@ pub fn break_transitive<'a, Db: DbHandle>(
|
|||||||
error: error.clone(),
|
error: error.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
status.save(&mut tx).await?;
|
receipts
|
||||||
|
.dependency_errors
|
||||||
|
.set(&mut tx, dependency_errors, id)
|
||||||
|
.await?;
|
||||||
|
|
||||||
tx.save().await?;
|
tx.save().await?;
|
||||||
break_all_dependents_transitive(db, id, DependencyError::Transitive, breakages).await?;
|
break_all_dependents_transitive(
|
||||||
|
db,
|
||||||
|
id,
|
||||||
|
DependencyError::Transitive,
|
||||||
|
breakages,
|
||||||
|
receipts,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
status.save(&mut tx).await?;
|
receipts
|
||||||
|
.dependency_errors
|
||||||
|
.set(&mut tx, dependency_errors, id)
|
||||||
|
.await?;
|
||||||
|
|
||||||
tx.save().await?;
|
tx.save().await?;
|
||||||
}
|
}
|
||||||
@@ -808,68 +944,52 @@ pub fn break_transitive<'a, Db: DbHandle>(
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(ctx, db))]
|
#[instrument(skip(ctx, db, locks))]
|
||||||
pub async fn heal_all_dependents_transitive<'a, Db: DbHandle>(
|
pub async fn heal_all_dependents_transitive<'a, Db: DbHandle>(
|
||||||
ctx: &'a RpcContext,
|
ctx: &'a RpcContext,
|
||||||
db: &'a mut Db,
|
db: &'a mut Db,
|
||||||
id: &'a PackageId,
|
id: &'a PackageId,
|
||||||
|
locks: &'a DependencyReceipt,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
for dependent in crate::db::DatabaseModel::new()
|
let dependents = locks
|
||||||
.package_data()
|
.current_dependents
|
||||||
.idx_model(id)
|
.get(db, id)
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.expect(db)
|
|
||||||
.await?
|
.await?
|
||||||
.current_dependents()
|
.ok_or_else(not_found)?;
|
||||||
.keys(db, true)
|
for dependent in dependents.keys().filter(|dependent| id != *dependent) {
|
||||||
.await?
|
heal_transitive(ctx, db, dependent, id, locks).await?;
|
||||||
.into_iter()
|
|
||||||
.filter(|dependent| id != dependent)
|
|
||||||
{
|
|
||||||
heal_transitive(ctx, db, &dependent, id).await?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(ctx, db))]
|
#[instrument(skip(ctx, db, receipts))]
|
||||||
pub fn heal_transitive<'a, Db: DbHandle>(
|
pub fn heal_transitive<'a, Db: DbHandle>(
|
||||||
ctx: &'a RpcContext,
|
ctx: &'a RpcContext,
|
||||||
db: &'a mut Db,
|
db: &'a mut Db,
|
||||||
id: &'a PackageId,
|
id: &'a PackageId,
|
||||||
dependency: &'a PackageId,
|
dependency: &'a PackageId,
|
||||||
|
receipts: &'a DependencyReceipt,
|
||||||
) -> BoxFuture<'a, Result<(), Error>> {
|
) -> BoxFuture<'a, Result<(), Error>> {
|
||||||
async move {
|
async move {
|
||||||
let mut tx = db.begin().await?;
|
let mut status = receipts.status.get(db, id).await?.ok_or_else(not_found)?;
|
||||||
let model = crate::db::DatabaseModel::new()
|
|
||||||
.package_data()
|
|
||||||
.idx_model(id)
|
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.expect(&mut tx)
|
|
||||||
.await?;
|
|
||||||
let mut status = model.clone().status().get_mut(&mut tx).await?;
|
|
||||||
|
|
||||||
let old = status.dependency_errors.0.remove(dependency);
|
let old = status.dependency_errors.0.remove(dependency);
|
||||||
|
|
||||||
if let Some(old) = old {
|
if let Some(old) = old {
|
||||||
let info = model
|
let info = receipts
|
||||||
.manifest()
|
.dependency
|
||||||
.dependencies()
|
.get(db, (id, dependency))
|
||||||
.idx_model(dependency)
|
|
||||||
.expect(&mut tx)
|
|
||||||
.await?
|
.await?
|
||||||
.get(&mut tx, true)
|
.ok_or_else(not_found)?;
|
||||||
.await?;
|
|
||||||
if let Some(new) = old
|
if let Some(new) = old
|
||||||
.try_heal(ctx, &mut tx, id, dependency, None, &*info)
|
.try_heal(ctx, db, id, dependency, None, &info, &receipts.try_heal)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
status.dependency_errors.0.insert(dependency.clone(), new);
|
status.dependency_errors.0.insert(dependency.clone(), new);
|
||||||
status.save(&mut tx).await?;
|
receipts.status.set(db, status, id).await?;
|
||||||
tx.save().await?;
|
|
||||||
} else {
|
} else {
|
||||||
status.save(&mut tx).await?;
|
receipts.status.set(db, status, id).await?;
|
||||||
tx.save().await?;
|
heal_all_dependents_transitive(ctx, db, id, receipts).await?;
|
||||||
heal_all_dependents_transitive(ctx, db, id).await?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,6 +1001,7 @@ pub fn heal_transitive<'a, Db: DbHandle>(
|
|||||||
pub async fn reconfigure_dependents_with_live_pointers(
|
pub async fn reconfigure_dependents_with_live_pointers(
|
||||||
ctx: &RpcContext,
|
ctx: &RpcContext,
|
||||||
mut tx: impl DbHandle,
|
mut tx: impl DbHandle,
|
||||||
|
receipts: &ConfigReceipts,
|
||||||
pde: &InstalledPackageDataEntry,
|
pde: &InstalledPackageDataEntry,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let dependents = &pde.current_dependents;
|
let dependents = &pde.current_dependents;
|
||||||
@@ -903,9 +1024,60 @@ pub async fn reconfigure_dependents_with_live_pointers(
|
|||||||
false,
|
false,
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
|
receipts,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DependencyReceipt {
|
||||||
|
pub try_heal: TryHealReceipts,
|
||||||
|
current_dependents: LockReceipt<BTreeMap<PackageId, CurrentDependencyInfo>, String>,
|
||||||
|
status: LockReceipt<Status, String>,
|
||||||
|
dependency: LockReceipt<DepInfo, (String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DependencyReceipt {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(locks: &mut Vec<LockTargetId>) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
let try_heal = TryHealReceipts::setup(locks);
|
||||||
|
let dependency = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest().dependencies().star())
|
||||||
|
.make_locker(LockType::Read)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let current_dependents = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.current_dependents())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let status = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.status())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
try_heal: try_heal(skeleton_key)?,
|
||||||
|
current_dependents: current_dependents.verify(skeleton_key)?,
|
||||||
|
status: status.verify(skeleton_key)?,
|
||||||
|
dependency: dependency.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use futures::FutureExt;
|
|||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::{Error, ResultExt};
|
use crate::Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use patch_db::{DbHandle, LockReceipt, LockType};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::context::rpc::RpcContextConfig;
|
use crate::context::rpc::RpcContextConfig;
|
||||||
@@ -23,6 +24,48 @@ pub async fn check_time_is_synchronized() -> Result<bool, Error> {
|
|||||||
== "NTPSynchronized=yes")
|
== "NTPSynchronized=yes")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct InitReceipts {
|
||||||
|
pub server_version: LockReceipt<crate::util::Version, ()>,
|
||||||
|
pub version_range: LockReceipt<emver::VersionRange, ()>,
|
||||||
|
pub last_wifi_region: LockReceipt<Option<isocountry::CountryCode>, ()>,
|
||||||
|
pub status_info: LockReceipt<ServerStatus, ()>,
|
||||||
|
}
|
||||||
|
impl InitReceipts {
|
||||||
|
pub async fn new(db: &mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let server_version = crate::db::DatabaseModel::new()
|
||||||
|
.server_info()
|
||||||
|
.version()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
let version_range = crate::db::DatabaseModel::new()
|
||||||
|
.server_info()
|
||||||
|
.eos_version_compat()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
let last_wifi_region = crate::db::DatabaseModel::new()
|
||||||
|
.server_info()
|
||||||
|
.last_wifi_region()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
let status_info = crate::db::DatabaseModel::new()
|
||||||
|
.server_info()
|
||||||
|
.status_info()
|
||||||
|
.into_model()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(&mut locks);
|
||||||
|
|
||||||
|
let skeleton_key = db.lock_all(locks).await?;
|
||||||
|
Ok(Self {
|
||||||
|
server_version: server_version.verify(&skeleton_key)?,
|
||||||
|
version_range: version_range.verify(&skeleton_key)?,
|
||||||
|
status_info: status_info.verify(&skeleton_key)?,
|
||||||
|
last_wifi_region: last_wifi_region.verify(&skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error> {
|
pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error> {
|
||||||
let should_rebuild = tokio::fs::metadata(SYSTEM_REBUILD_PATH).await.is_ok();
|
let should_rebuild = tokio::fs::metadata(SYSTEM_REBUILD_PATH).await.is_ok();
|
||||||
let secret_store = cfg.secret_store().await?;
|
let secret_store = cfg.secret_store().await?;
|
||||||
@@ -87,13 +130,13 @@ pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error
|
|||||||
let db = cfg.db(&secret_store, product_key).await?;
|
let db = cfg.db(&secret_store, product_key).await?;
|
||||||
|
|
||||||
let mut handle = db.handle();
|
let mut handle = db.handle();
|
||||||
|
let receipts = InitReceipts::new(&mut handle).await?;
|
||||||
|
|
||||||
crate::net::wifi::synchronize_wpa_supplicant_conf(
|
crate::net::wifi::synchronize_wpa_supplicant_conf(
|
||||||
&cfg.datadir().join("main"),
|
&cfg.datadir().join("main"),
|
||||||
&*crate::db::DatabaseModel::new()
|
&receipts
|
||||||
.server_info()
|
.last_wifi_region
|
||||||
.last_wifi_region()
|
.get(&mut handle)
|
||||||
.get(&mut handle, false)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|_e| {
|
.map_err(|_e| {
|
||||||
Error::new(
|
Error::new(
|
||||||
@@ -104,16 +147,17 @@ pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
tracing::info!("Synchronized wpa_supplicant.conf");
|
tracing::info!("Synchronized wpa_supplicant.conf");
|
||||||
let mut info = crate::db::DatabaseModel::new()
|
receipts
|
||||||
.server_info()
|
.status_info
|
||||||
.get_mut(&mut handle)
|
.set(
|
||||||
|
&mut handle,
|
||||||
|
ServerStatus {
|
||||||
|
backing_up: false,
|
||||||
|
updated: false,
|
||||||
|
update_progress: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
info.status_info = ServerStatus {
|
|
||||||
backing_up: false,
|
|
||||||
updated: false,
|
|
||||||
update_progress: None,
|
|
||||||
};
|
|
||||||
info.save(&mut handle).await?;
|
|
||||||
|
|
||||||
let mut warn_time_not_synced = true;
|
let mut warn_time_not_synced = true;
|
||||||
for _ in 0..60 {
|
for _ in 0..60 {
|
||||||
@@ -127,7 +171,7 @@ pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error
|
|||||||
tracing::warn!("Timed out waiting for system time to synchronize");
|
tracing::warn!("Timed out waiting for system time to synchronize");
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::version::init(&mut handle).await?;
|
crate::version::init(&mut handle, &receipts).await?;
|
||||||
|
|
||||||
if should_rebuild {
|
if should_rebuild {
|
||||||
tokio::fs::remove_file(SYSTEM_REBUILD_PATH).await?;
|
tokio::fs::remove_file(SYSTEM_REBUILD_PATH).await?;
|
||||||
|
|||||||
@@ -1,21 +1,65 @@
|
|||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
use bollard::image::ListImagesOptions;
|
use bollard::image::ListImagesOptions;
|
||||||
use color_eyre::eyre::eyre;
|
use patch_db::{DbHandle, LockReceipt, LockTargetId, LockType, PatchDbHandle, Verifier};
|
||||||
use patch_db::{DbHandle, LockType, PatchDbHandle};
|
|
||||||
use sqlx::{Executor, Sqlite};
|
use sqlx::{Executor, Sqlite};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use super::{PKG_ARCHIVE_DIR, PKG_DOCKER_DIR};
|
use super::{PKG_ARCHIVE_DIR, PKG_DOCKER_DIR};
|
||||||
use crate::context::RpcContext;
|
use crate::db::model::{InstalledPackageDataEntry, PackageDataEntry};
|
||||||
use crate::db::model::{CurrentDependencyInfo, InstalledPackageDataEntry, PackageDataEntry};
|
use crate::{config::not_found, dependencies::reconfigure_dependents_with_live_pointers};
|
||||||
use crate::dependencies::reconfigure_dependents_with_live_pointers;
|
use crate::{config::ConfigReceipts, context::RpcContext};
|
||||||
use crate::error::ErrorCollection;
|
use crate::{
|
||||||
use crate::s9pk::manifest::{Manifest, PackageId};
|
db::model::AllPackageData,
|
||||||
use crate::util::{Apply, Version};
|
s9pk::manifest::{Manifest, PackageId},
|
||||||
use crate::Error;
|
};
|
||||||
|
use crate::{db::model::CurrentDependencyInfo, error::ErrorCollection};
|
||||||
|
use crate::{
|
||||||
|
dependencies::DependencyErrors,
|
||||||
|
util::{Apply, Version},
|
||||||
|
};
|
||||||
|
use crate::{dependencies::TryHealReceipts, Error};
|
||||||
|
|
||||||
#[instrument(skip(ctx, db, deps))]
|
pub struct UpdateDependencyReceipts {
|
||||||
|
try_heal: TryHealReceipts,
|
||||||
|
dependency_errors: LockReceipt<DependencyErrors, String>,
|
||||||
|
manifest: LockReceipt<Manifest, String>,
|
||||||
|
}
|
||||||
|
impl UpdateDependencyReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(locks: &mut Vec<LockTargetId>) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
let dependency_errors = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.status().dependency_errors())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let manifest = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.manifest())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let try_heal = TryHealReceipts::setup(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
dependency_errors: dependency_errors.verify(skeleton_key)?,
|
||||||
|
manifest: manifest.verify(skeleton_key)?,
|
||||||
|
try_heal: try_heal(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(ctx, db, deps, receipts))]
|
||||||
pub async fn update_dependency_errors_of_dependents<
|
pub async fn update_dependency_errors_of_dependents<
|
||||||
'a,
|
'a,
|
||||||
Db: DbHandle,
|
Db: DbHandle,
|
||||||
@@ -25,50 +69,31 @@ pub async fn update_dependency_errors_of_dependents<
|
|||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
id: &PackageId,
|
id: &PackageId,
|
||||||
deps: I,
|
deps: I,
|
||||||
|
receipts: &UpdateDependencyReceipts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
for dep in deps {
|
for dep in deps {
|
||||||
if let Some(man) = &*crate::db::DatabaseModel::new()
|
if let Some(man) = receipts.manifest.get(db, dep).await? {
|
||||||
.package_data()
|
|
||||||
.idx_model(&dep)
|
|
||||||
.and_then(|m| m.installed())
|
|
||||||
.map::<_, Manifest>(|m| m.manifest())
|
|
||||||
.get(db, true)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
if let Err(e) = if let Some(info) = man.dependencies.0.get(id) {
|
if let Err(e) = if let Some(info) = man.dependencies.0.get(id) {
|
||||||
info.satisfied(ctx, db, id, None, dep).await?
|
info.satisfied(ctx, db, id, None, dep, &receipts.try_heal)
|
||||||
|
.await?
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
} {
|
} {
|
||||||
let mut errs = crate::db::DatabaseModel::new()
|
let mut errs = receipts
|
||||||
.package_data()
|
.dependency_errors
|
||||||
.idx_model(&dep)
|
.get(db, dep)
|
||||||
.expect(db)
|
|
||||||
.await?
|
.await?
|
||||||
.installed()
|
.ok_or_else(not_found)?;
|
||||||
.expect(db)
|
|
||||||
.await?
|
|
||||||
.status()
|
|
||||||
.dependency_errors()
|
|
||||||
.get_mut(db)
|
|
||||||
.await?;
|
|
||||||
errs.0.insert(id.clone(), e);
|
errs.0.insert(id.clone(), e);
|
||||||
errs.save(db).await?;
|
receipts.dependency_errors.set(db, errs, dep).await?
|
||||||
} else {
|
} else {
|
||||||
let mut errs = crate::db::DatabaseModel::new()
|
let mut errs = receipts
|
||||||
.package_data()
|
.dependency_errors
|
||||||
.idx_model(&dep)
|
.get(db, dep)
|
||||||
.expect(db)
|
|
||||||
.await?
|
.await?
|
||||||
.installed()
|
.ok_or_else(not_found)?;
|
||||||
.expect(db)
|
|
||||||
.await?
|
|
||||||
.status()
|
|
||||||
.dependency_errors()
|
|
||||||
.get_mut(db)
|
|
||||||
.await?;
|
|
||||||
errs.0.remove(id);
|
errs.0.remove(id);
|
||||||
errs.save(db).await?;
|
receipts.dependency_errors.set(db, errs, dep).await?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,24 +152,50 @@ pub async fn cleanup(ctx: &RpcContext, id: &PackageId, version: &Version) -> Res
|
|||||||
errors.into_result()
|
errors.into_result()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(ctx, db))]
|
pub struct CleanupFailedReceipts {
|
||||||
|
package_data_entry: LockReceipt<PackageDataEntry, String>,
|
||||||
|
package_entries: LockReceipt<AllPackageData, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CleanupFailedReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(locks: &mut Vec<LockTargetId>) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
let package_data_entry = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let package_entries = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
package_data_entry: package_data_entry.verify(skeleton_key).unwrap(),
|
||||||
|
package_entries: package_entries.verify(skeleton_key).unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(ctx, db, receipts))]
|
||||||
pub async fn cleanup_failed<Db: DbHandle>(
|
pub async fn cleanup_failed<Db: DbHandle>(
|
||||||
ctx: &RpcContext,
|
ctx: &RpcContext,
|
||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
id: &PackageId,
|
id: &PackageId,
|
||||||
|
receipts: &CleanupFailedReceipts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
crate::db::DatabaseModel::new()
|
let pde = receipts
|
||||||
.package_data()
|
.package_data_entry
|
||||||
.lock(db, LockType::Write)
|
.get(db, id)
|
||||||
.await?;
|
|
||||||
let pde = crate::db::DatabaseModel::new()
|
|
||||||
.package_data()
|
|
||||||
.idx_model(id)
|
|
||||||
.expect(db)
|
|
||||||
.await?
|
.await?
|
||||||
.get(db, true)
|
.ok_or_else(not_found)?;
|
||||||
.await?
|
|
||||||
.into_owned();
|
|
||||||
if let Some(manifest) = match &pde {
|
if let Some(manifest) = match &pde {
|
||||||
PackageDataEntry::Installing { manifest, .. }
|
PackageDataEntry::Installing { manifest, .. }
|
||||||
| PackageDataEntry::Restoring { manifest, .. } => Some(manifest),
|
| PackageDataEntry::Restoring { manifest, .. } => Some(manifest),
|
||||||
@@ -173,26 +224,29 @@ pub async fn cleanup_failed<Db: DbHandle>(
|
|||||||
|
|
||||||
match pde {
|
match pde {
|
||||||
PackageDataEntry::Installing { .. } | PackageDataEntry::Restoring { .. } => {
|
PackageDataEntry::Installing { .. } | PackageDataEntry::Restoring { .. } => {
|
||||||
crate::db::DatabaseModel::new()
|
let mut entries = receipts
|
||||||
.package_data()
|
.package_entries
|
||||||
.remove(db, id)
|
.get(db, id)
|
||||||
.await?;
|
.await?
|
||||||
|
.ok_or_else(not_found)?;
|
||||||
|
entries.0.remove(id);
|
||||||
|
receipts.package_entries.set(db, entries, id).await?;
|
||||||
}
|
}
|
||||||
PackageDataEntry::Updating {
|
PackageDataEntry::Updating {
|
||||||
installed,
|
installed,
|
||||||
static_files,
|
static_files,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
crate::db::DatabaseModel::new()
|
receipts
|
||||||
.package_data()
|
.package_data_entry
|
||||||
.idx_model(id)
|
.set(
|
||||||
.put(
|
|
||||||
db,
|
db,
|
||||||
&PackageDataEntry::Installed {
|
PackageDataEntry::Installed {
|
||||||
manifest: installed.manifest.clone(),
|
manifest: installed.manifest.clone(),
|
||||||
installed,
|
installed,
|
||||||
static_files,
|
static_files,
|
||||||
},
|
},
|
||||||
|
id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
@@ -202,7 +256,7 @@ pub async fn cleanup_failed<Db: DbHandle>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(db, current_dependencies))]
|
#[instrument(skip(db, current_dependencies, current_dependent_receipt))]
|
||||||
pub async fn remove_from_current_dependents_lists<
|
pub async fn remove_from_current_dependents_lists<
|
||||||
'a,
|
'a,
|
||||||
Db: DbHandle,
|
Db: DbHandle,
|
||||||
@@ -211,29 +265,69 @@ pub async fn remove_from_current_dependents_lists<
|
|||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
id: &'a PackageId,
|
id: &'a PackageId,
|
||||||
current_dependencies: I,
|
current_dependencies: I,
|
||||||
|
current_dependent_receipt: &LockReceipt<BTreeMap<PackageId, CurrentDependencyInfo>, String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
for dep in current_dependencies.into_iter().chain(std::iter::once(id)) {
|
for dep in current_dependencies.into_iter().chain(std::iter::once(id)) {
|
||||||
if let Some(current_dependents) = crate::db::DatabaseModel::new()
|
if let Some(mut current_dependents) = current_dependent_receipt.get(db, dep).await? {
|
||||||
.package_data()
|
if current_dependents.remove(id).is_some() {
|
||||||
.idx_model(dep)
|
current_dependent_receipt
|
||||||
.and_then(|m| m.installed())
|
.set(db, current_dependents, dep)
|
||||||
.map::<_, BTreeMap<PackageId, CurrentDependencyInfo>>(|m| m.current_dependents())
|
.await?;
|
||||||
.check(db)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
if current_dependents
|
|
||||||
.clone()
|
|
||||||
.idx_model(id)
|
|
||||||
.exists(db, true)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
current_dependents.remove(db, id).await?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
pub struct UninstallReceipts {
|
||||||
|
config: ConfigReceipts,
|
||||||
|
removing: LockReceipt<InstalledPackageDataEntry, ()>,
|
||||||
|
packages: LockReceipt<AllPackageData, ()>,
|
||||||
|
current_dependents: LockReceipt<BTreeMap<PackageId, CurrentDependencyInfo>, String>,
|
||||||
|
update_depenency_receipts: UpdateDependencyReceipts,
|
||||||
|
}
|
||||||
|
impl UninstallReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle, id: &PackageId) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks, id);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
locks: &mut Vec<LockTargetId>,
|
||||||
|
id: &PackageId,
|
||||||
|
) -> impl FnOnce(&Verifier) -> Result<Self, Error> {
|
||||||
|
let config = ConfigReceipts::setup(locks);
|
||||||
|
let removing = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.idx_model(id)
|
||||||
|
.and_then(|pde| pde.removing())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
let current_dependents = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.star()
|
||||||
|
.installed()
|
||||||
|
.map(|x| x.current_dependents())
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let packages = crate::db::DatabaseModel::new()
|
||||||
|
.package_data()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
let update_depenency_receipts = UpdateDependencyReceipts::setup(locks);
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
config: config(skeleton_key)?,
|
||||||
|
removing: removing.verify(skeleton_key)?,
|
||||||
|
current_dependents: current_dependents.verify(skeleton_key)?,
|
||||||
|
update_depenency_receipts: update_depenency_receipts(skeleton_key)?,
|
||||||
|
packages: packages.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#[instrument(skip(ctx, secrets, db))]
|
#[instrument(skip(ctx, secrets, db))]
|
||||||
pub async fn uninstall<Ex>(
|
pub async fn uninstall<Ex>(
|
||||||
ctx: &RpcContext,
|
ctx: &RpcContext,
|
||||||
@@ -245,37 +339,24 @@ where
|
|||||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||||
{
|
{
|
||||||
let mut tx = db.begin().await?;
|
let mut tx = db.begin().await?;
|
||||||
crate::db::DatabaseModel::new()
|
let receipts = UninstallReceipts::new(&mut tx, id).await?;
|
||||||
.package_data()
|
let entry = receipts.removing.get(&mut tx).await?;
|
||||||
.lock(&mut tx, LockType::Write)
|
|
||||||
.await?;
|
|
||||||
let entry = crate::db::DatabaseModel::new()
|
|
||||||
.package_data()
|
|
||||||
.idx_model(id)
|
|
||||||
.and_then(|pde| pde.removing())
|
|
||||||
.get(&mut tx, true)
|
|
||||||
.await?
|
|
||||||
.into_owned()
|
|
||||||
.ok_or_else(|| {
|
|
||||||
Error::new(
|
|
||||||
eyre!("Package not in removing state: {}", id),
|
|
||||||
crate::ErrorKind::NotFound,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
cleanup(ctx, &entry.manifest.id, &entry.manifest.version).await?;
|
cleanup(ctx, &entry.manifest.id, &entry.manifest.version).await?;
|
||||||
|
|
||||||
crate::db::DatabaseModel::new()
|
let packages = {
|
||||||
.package_data()
|
let mut packages = receipts.packages.get(&mut tx).await?;
|
||||||
.remove(&mut tx, id)
|
packages.0.remove(id);
|
||||||
.await?;
|
packages
|
||||||
|
};
|
||||||
|
receipts.packages.set(&mut tx, packages).await?;
|
||||||
// once we have removed the package entry, we can change all the dependent pointers to null
|
// once we have removed the package entry, we can change all the dependent pointers to null
|
||||||
reconfigure_dependents_with_live_pointers(ctx, &mut tx, &entry).await?;
|
reconfigure_dependents_with_live_pointers(ctx, &mut tx, &receipts.config, &entry).await?;
|
||||||
|
|
||||||
remove_from_current_dependents_lists(
|
remove_from_current_dependents_lists(
|
||||||
&mut tx,
|
&mut tx,
|
||||||
&entry.manifest.id,
|
&entry.manifest.id,
|
||||||
entry.current_dependencies.keys(),
|
entry.current_dependencies.keys(),
|
||||||
|
&receipts.current_dependents,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
update_dependency_errors_of_dependents(
|
update_dependency_errors_of_dependents(
|
||||||
@@ -283,6 +364,7 @@ where
|
|||||||
&mut tx,
|
&mut tx,
|
||||||
&entry.manifest.id,
|
&entry.manifest.id,
|
||||||
entry.current_dependents.keys(),
|
entry.current_dependents.keys(),
|
||||||
|
&receipts.update_depenency_receipts,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let volumes = ctx
|
let volumes = ctx
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use futures::{FutureExt, StreamExt, TryStreamExt};
|
|||||||
use http::header::CONTENT_LENGTH;
|
use http::header::CONTENT_LENGTH;
|
||||||
use http::{Request, Response, StatusCode};
|
use http::{Request, Response, StatusCode};
|
||||||
use hyper::Body;
|
use hyper::Body;
|
||||||
use patch_db::{DbHandle, LockType};
|
use patch_db::{DbHandle, LockReceipt, LockType};
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use rpc_toolkit::yajrc::RpcError;
|
use rpc_toolkit::yajrc::RpcError;
|
||||||
use rpc_toolkit::{command, Context};
|
use rpc_toolkit::{command, Context};
|
||||||
@@ -25,8 +25,6 @@ use tokio_stream::wrappers::ReadDirStream;
|
|||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use self::cleanup::{cleanup_failed, remove_from_current_dependents_lists};
|
use self::cleanup::{cleanup_failed, remove_from_current_dependents_lists};
|
||||||
use crate::context::{CliContext, RpcContext};
|
|
||||||
use crate::core::rpc_continuations::{RequestGuid, RpcContinuation};
|
|
||||||
use crate::db::model::{
|
use crate::db::model::{
|
||||||
CurrentDependencyInfo, InstalledPackageDataEntry, PackageDataEntry, RecoveredPackageInfo,
|
CurrentDependencyInfo, InstalledPackageDataEntry, PackageDataEntry, RecoveredPackageInfo,
|
||||||
StaticDependencyInfo, StaticFiles,
|
StaticDependencyInfo, StaticFiles,
|
||||||
@@ -47,16 +45,24 @@ use crate::util::serde::{display_serializable, IoFormat, Port};
|
|||||||
use crate::util::{display_none, AsyncFileExt, Version};
|
use crate::util::{display_none, AsyncFileExt, Version};
|
||||||
use crate::version::{Current, VersionT};
|
use crate::version::{Current, VersionT};
|
||||||
use crate::volume::asset_dir;
|
use crate::volume::asset_dir;
|
||||||
|
use crate::{
|
||||||
|
config::ConfigReceipts,
|
||||||
|
context::{CliContext, RpcContext},
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
core::rpc_continuations::{RequestGuid, RpcContinuation},
|
||||||
|
dependencies::BreakTransitiveReceipts,
|
||||||
|
};
|
||||||
use crate::{Error, ErrorKind, ResultExt};
|
use crate::{Error, ErrorKind, ResultExt};
|
||||||
|
|
||||||
pub mod cleanup;
|
pub mod cleanup;
|
||||||
pub mod progress;
|
pub mod progress;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
||||||
pub const PKG_ARCHIVE_DIR: &'static str = "package-data/archive";
|
pub const PKG_ARCHIVE_DIR: &str = "package-data/archive";
|
||||||
pub const PKG_PUBLIC_DIR: &'static str = "package-data/public";
|
pub const PKG_PUBLIC_DIR: &str = "package-data/public";
|
||||||
pub const PKG_DOCKER_DIR: &'static str = "package-data/docker";
|
pub const PKG_DOCKER_DIR: &str = "package-data/docker";
|
||||||
pub const PKG_WASM_DIR: &'static str = "package-data/wasm";
|
pub const PKG_WASM_DIR: &str = "package-data/wasm";
|
||||||
|
|
||||||
#[command(display(display_serializable))]
|
#[command(display(display_serializable))]
|
||||||
pub async fn list(#[context] ctx: RpcContext) -> Result<Vec<(PackageId, Version)>, Error> {
|
pub async fn list(#[context] ctx: RpcContext) -> Result<Vec<(PackageId, Version)>, Error> {
|
||||||
@@ -447,7 +453,7 @@ pub async fn sideload(
|
|||||||
});
|
});
|
||||||
let cont = RpcContinuation {
|
let cont = RpcContinuation {
|
||||||
created_at: Instant::now(), // TODO
|
created_at: Instant::now(), // TODO
|
||||||
handler: handler,
|
handler,
|
||||||
};
|
};
|
||||||
// gc the map
|
// gc the map
|
||||||
let mut guard = ctx.rpc_stream_continuations.lock().await;
|
let mut guard = ctx.rpc_stream_continuations.lock().await;
|
||||||
@@ -562,8 +568,15 @@ pub async fn uninstall_dry(
|
|||||||
let mut db = ctx.db.handle();
|
let mut db = ctx.db.handle();
|
||||||
let mut tx = db.begin().await?;
|
let mut tx = db.begin().await?;
|
||||||
let mut breakages = BTreeMap::new();
|
let mut breakages = BTreeMap::new();
|
||||||
break_all_dependents_transitive(&mut tx, &id, DependencyError::NotInstalled, &mut breakages)
|
let receipts = BreakTransitiveReceipts::new(&mut tx).await?;
|
||||||
.await?;
|
break_all_dependents_transitive(
|
||||||
|
&mut tx,
|
||||||
|
&id,
|
||||||
|
DependencyError::NotInstalled,
|
||||||
|
&mut breakages,
|
||||||
|
&receipts,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
tx.abort().await?;
|
tx.abort().await?;
|
||||||
|
|
||||||
@@ -678,6 +691,35 @@ pub async fn delete_recovered(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DownloadInstallReceipts {
|
||||||
|
package_receipts: crate::db::package::PackageReceipts,
|
||||||
|
manifest_receipts: crate::db::package::ManifestReceipts,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DownloadInstallReceipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle, id: &PackageId) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks, id);
|
||||||
|
Ok(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 package_receipts = crate::db::package::PackageReceipts::setup(locks);
|
||||||
|
let manifest_receipts = crate::db::package::ManifestReceipts::setup(locks, id);
|
||||||
|
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
package_receipts: package_receipts(skeleton_key)?,
|
||||||
|
manifest_receipts: manifest_receipts(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip(ctx, temp_manifest, s9pk))]
|
#[instrument(skip(ctx, temp_manifest, s9pk))]
|
||||||
pub async fn download_install_s9pk(
|
pub async fn download_install_s9pk(
|
||||||
ctx: &RpcContext,
|
ctx: &RpcContext,
|
||||||
@@ -692,14 +734,14 @@ pub async fn download_install_s9pk(
|
|||||||
if let Err(e) = async {
|
if let Err(e) = async {
|
||||||
let mut db_handle = ctx.db.handle();
|
let mut db_handle = ctx.db.handle();
|
||||||
let mut tx = db_handle.begin().await?;
|
let mut tx = db_handle.begin().await?;
|
||||||
|
let receipts = DownloadInstallReceipts::new(&mut tx, &pkg_id).await?;
|
||||||
// Build set of existing manifests
|
// Build set of existing manifests
|
||||||
let mut manifests = Vec::new();
|
let mut manifests = Vec::new();
|
||||||
for pkg in crate::db::package::get_packages(&mut tx).await? {
|
for pkg in crate::db::package::get_packages(&mut tx, &receipts.package_receipts).await? {
|
||||||
match crate::db::package::get_manifest(&mut tx, &pkg).await? {
|
if let Some(m) =
|
||||||
Some(m) => {
|
crate::db::package::get_manifest(&mut tx, &pkg, &receipts.manifest_receipts).await?
|
||||||
manifests.push(m);
|
{
|
||||||
}
|
manifests.push(m);
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Build map of current port -> ssl mappings
|
// Build map of current port -> ssl mappings
|
||||||
@@ -732,6 +774,7 @@ pub async fn download_install_s9pk(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
drop(receipts);
|
||||||
tx.save().await?;
|
tx.save().await?;
|
||||||
drop(db_handle);
|
drop(db_handle);
|
||||||
|
|
||||||
@@ -792,8 +835,9 @@ pub async fn download_install_s9pk(
|
|||||||
{
|
{
|
||||||
let mut handle = ctx.db.handle();
|
let mut handle = ctx.db.handle();
|
||||||
let mut tx = handle.begin().await?;
|
let mut tx = handle.begin().await?;
|
||||||
|
let receipts = cleanup::CleanupFailedReceipts::new(&mut tx).await?;
|
||||||
|
|
||||||
if let Err(e) = cleanup_failed(&ctx, &mut tx, pkg_id).await {
|
if let Err(e) = cleanup_failed(&ctx, &mut tx, pkg_id, &receipts).await {
|
||||||
tracing::error!("Failed to clean up {}@{}: {}", pkg_id, version, e);
|
tracing::error!("Failed to clean up {}@{}: {}", pkg_id, version, e);
|
||||||
tracing::debug!("{:?}", e);
|
tracing::debug!("{:?}", e);
|
||||||
} else {
|
} else {
|
||||||
@@ -805,6 +849,39 @@ pub async fn download_install_s9pk(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct InstallS9Receipts {
|
||||||
|
config: ConfigReceipts,
|
||||||
|
|
||||||
|
recovered_packages: LockReceipt<BTreeMap<PackageId, RecoveredPackageInfo>, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstallS9Receipts {
|
||||||
|
pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result<Self, Error> {
|
||||||
|
let mut locks = Vec::new();
|
||||||
|
|
||||||
|
let setup = Self::setup(&mut locks);
|
||||||
|
Ok(setup(&db.lock_all(locks).await?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
locks: &mut Vec<patch_db::LockTargetId>,
|
||||||
|
) -> impl FnOnce(&patch_db::Verifier) -> Result<Self, Error> {
|
||||||
|
let config = ConfigReceipts::setup(locks);
|
||||||
|
|
||||||
|
let recovered_packages = crate::db::DatabaseModel::new()
|
||||||
|
.recovered_packages()
|
||||||
|
.make_locker(LockType::Write)
|
||||||
|
.add_to_keys(locks);
|
||||||
|
|
||||||
|
move |skeleton_key| {
|
||||||
|
Ok(Self {
|
||||||
|
config: config(skeleton_key)?,
|
||||||
|
recovered_packages: recovered_packages.verify(skeleton_key)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip(ctx, rdr))]
|
#[instrument(skip(ctx, rdr))]
|
||||||
pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
||||||
ctx: &RpcContext,
|
ctx: &RpcContext,
|
||||||
@@ -1183,6 +1260,8 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
pde.save(&mut tx).await?;
|
pde.save(&mut tx).await?;
|
||||||
|
let receipts = InstallS9Receipts::new(&mut tx).await?;
|
||||||
|
// UpdateDependencyReceipts
|
||||||
let mut dep_errs = model
|
let mut dep_errs = model
|
||||||
.expect(&mut tx)
|
.expect(&mut tx)
|
||||||
.await?
|
.await?
|
||||||
@@ -1193,7 +1272,14 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
|||||||
.dependency_errors()
|
.dependency_errors()
|
||||||
.get_mut(&mut tx)
|
.get_mut(&mut tx)
|
||||||
.await?;
|
.await?;
|
||||||
*dep_errs = DependencyErrors::init(ctx, &mut tx, &manifest, ¤t_dependencies).await?;
|
*dep_errs = DependencyErrors::init(
|
||||||
|
ctx,
|
||||||
|
&mut tx,
|
||||||
|
&manifest,
|
||||||
|
¤t_dependencies,
|
||||||
|
&receipts.config.try_heal_receipts,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
dep_errs.save(&mut tx).await?;
|
dep_errs.save(&mut tx).await?;
|
||||||
|
|
||||||
if let PackageDataEntry::Updating {
|
if let PackageDataEntry::Updating {
|
||||||
@@ -1244,6 +1330,7 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
|||||||
false,
|
false,
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
|
&receipts.config,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let mut main_status = crate::db::DatabaseModel::new()
|
let mut main_status = crate::db::DatabaseModel::new()
|
||||||
@@ -1261,9 +1348,20 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
|||||||
*main_status = prev.status.main;
|
*main_status = prev.status.main;
|
||||||
main_status.save(&mut tx).await?;
|
main_status.save(&mut tx).await?;
|
||||||
}
|
}
|
||||||
remove_from_current_dependents_lists(&mut tx, pkg_id, prev.current_dependencies.keys())
|
remove_from_current_dependents_lists(
|
||||||
.await?; // remove previous
|
&mut tx,
|
||||||
add_dependent_to_current_dependents_lists(&mut tx, pkg_id, ¤t_dependencies).await?; // add new
|
pkg_id,
|
||||||
|
prev.current_dependencies.keys(),
|
||||||
|
&receipts.config.current_dependents,
|
||||||
|
)
|
||||||
|
.await?; // remove previous
|
||||||
|
add_dependent_to_current_dependents_lists(
|
||||||
|
&mut tx,
|
||||||
|
pkg_id,
|
||||||
|
¤t_dependencies,
|
||||||
|
&receipts.config.current_dependents,
|
||||||
|
)
|
||||||
|
.await?; // add new
|
||||||
update_dependency_errors_of_dependents(
|
update_dependency_errors_of_dependents(
|
||||||
ctx,
|
ctx,
|
||||||
&mut tx,
|
&mut tx,
|
||||||
@@ -1272,6 +1370,7 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
|||||||
.keys()
|
.keys()
|
||||||
.chain(prev.current_dependents.keys())
|
.chain(prev.current_dependents.keys())
|
||||||
.collect::<BTreeSet<_>>(),
|
.collect::<BTreeSet<_>>(),
|
||||||
|
&receipts.config.update_dependency_receipts,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if &prev.manifest.version != version {
|
if &prev.manifest.version != version {
|
||||||
@@ -1290,39 +1389,84 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
|||||||
&manifest.volumes,
|
&manifest.volumes,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
add_dependent_to_current_dependents_lists(&mut tx, pkg_id, ¤t_dependencies).await?;
|
add_dependent_to_current_dependents_lists(
|
||||||
update_dependency_errors_of_dependents(ctx, &mut tx, pkg_id, current_dependents.keys())
|
&mut tx,
|
||||||
.await?;
|
pkg_id,
|
||||||
|
¤t_dependencies,
|
||||||
|
&receipts.config.current_dependents,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
update_dependency_errors_of_dependents(
|
||||||
|
ctx,
|
||||||
|
&mut tx,
|
||||||
|
pkg_id,
|
||||||
|
current_dependents.keys(),
|
||||||
|
&receipts.config.update_dependency_receipts,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
} else if let Some(recovered) = {
|
} else if let Some(recovered) = {
|
||||||
// solve taxonomy escalation
|
receipts
|
||||||
crate::db::DatabaseModel::new()
|
.recovered_packages
|
||||||
.recovered_packages()
|
.get(&mut tx)
|
||||||
.lock(&mut tx, LockType::Write)
|
|
||||||
.await?;
|
|
||||||
crate::db::DatabaseModel::new()
|
|
||||||
.recovered_packages()
|
|
||||||
.idx_model(pkg_id)
|
|
||||||
.get(&mut tx, true)
|
|
||||||
.await?
|
.await?
|
||||||
.into_owned()
|
.remove(pkg_id)
|
||||||
} {
|
} {
|
||||||
handle_recovered_package(recovered, manifest, ctx, pkg_id, version, &mut tx).await?;
|
handle_recovered_package(
|
||||||
add_dependent_to_current_dependents_lists(&mut tx, pkg_id, ¤t_dependencies).await?;
|
recovered,
|
||||||
update_dependency_errors_of_dependents(ctx, &mut tx, pkg_id, current_dependents.keys())
|
manifest,
|
||||||
.await?;
|
ctx,
|
||||||
|
pkg_id,
|
||||||
|
version,
|
||||||
|
&mut tx,
|
||||||
|
&receipts.config,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
add_dependent_to_current_dependents_lists(
|
||||||
|
&mut tx,
|
||||||
|
pkg_id,
|
||||||
|
¤t_dependencies,
|
||||||
|
&receipts.config.current_dependents,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
update_dependency_errors_of_dependents(
|
||||||
|
ctx,
|
||||||
|
&mut tx,
|
||||||
|
pkg_id,
|
||||||
|
current_dependents.keys(),
|
||||||
|
&receipts.config.update_dependency_receipts,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
add_dependent_to_current_dependents_lists(&mut tx, pkg_id, ¤t_dependencies).await?;
|
add_dependent_to_current_dependents_lists(
|
||||||
update_dependency_errors_of_dependents(ctx, &mut tx, pkg_id, current_dependents.keys())
|
&mut tx,
|
||||||
.await?;
|
pkg_id,
|
||||||
|
¤t_dependencies,
|
||||||
|
&receipts.config.current_dependents,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
update_dependency_errors_of_dependents(
|
||||||
|
ctx,
|
||||||
|
&mut tx,
|
||||||
|
pkg_id,
|
||||||
|
current_dependents.keys(),
|
||||||
|
&receipts.config.update_dependency_receipts,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::db::DatabaseModel::new()
|
let recovered_packages = {
|
||||||
.recovered_packages()
|
let mut r = receipts.recovered_packages.get(&mut tx).await?;
|
||||||
.remove(&mut tx, pkg_id)
|
r.remove(pkg_id);
|
||||||
|
r
|
||||||
|
};
|
||||||
|
receipts
|
||||||
|
.recovered_packages
|
||||||
|
.set(&mut tx, recovered_packages)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Some(installed) = pde.installed() {
|
if let Some(installed) = pde.installed() {
|
||||||
reconfigure_dependents_with_live_pointers(ctx, &mut tx, installed).await?;
|
reconfigure_dependents_with_live_pointers(ctx, &mut tx, &receipts.config, installed)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
sql_tx.commit().await?;
|
sql_tx.commit().await?;
|
||||||
@@ -1333,7 +1477,7 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(ctx, tx))]
|
#[instrument(skip(ctx, tx, receipts))]
|
||||||
async fn handle_recovered_package(
|
async fn handle_recovered_package(
|
||||||
recovered: RecoveredPackageInfo,
|
recovered: RecoveredPackageInfo,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
@@ -1341,6 +1485,7 @@ async fn handle_recovered_package(
|
|||||||
pkg_id: &PackageId,
|
pkg_id: &PackageId,
|
||||||
version: &Version,
|
version: &Version,
|
||||||
tx: &mut patch_db::Transaction<&mut patch_db::PatchDbHandle>,
|
tx: &mut patch_db::Transaction<&mut patch_db::PatchDbHandle>,
|
||||||
|
receipts: &ConfigReceipts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let configured = if let Some(migration) =
|
let configured = if let Some(migration) =
|
||||||
manifest
|
manifest
|
||||||
@@ -1361,6 +1506,7 @@ async fn handle_recovered_package(
|
|||||||
false,
|
false,
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
|
&receipts,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,12 @@ pub async fn dry(
|
|||||||
let mut db = ctx.db.handle();
|
let mut db = ctx.db.handle();
|
||||||
let mut tx = db.begin().await?;
|
let mut tx = db.begin().await?;
|
||||||
let mut breakages = BTreeMap::new();
|
let mut breakages = BTreeMap::new();
|
||||||
|
let receipts = crate::dependencies::BreakTransitiveReceipts::new(&mut tx).await?;
|
||||||
crate::db::DatabaseModel::new()
|
crate::db::DatabaseModel::new()
|
||||||
.package_data()
|
.package_data()
|
||||||
.lock(&mut tx, LockType::Read)
|
.lock(&mut tx, LockType::Read)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for dependent in crate::db::DatabaseModel::new()
|
for dependent in crate::db::DatabaseModel::new()
|
||||||
.package_data()
|
.package_data()
|
||||||
.idx_model(&id)
|
.idx_model(&id)
|
||||||
@@ -65,6 +67,7 @@ pub async fn dry(
|
|||||||
received: version.clone(),
|
received: version.clone(),
|
||||||
},
|
},
|
||||||
&mut breakages,
|
&mut breakages,
|
||||||
|
&receipts,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||||||
use patch_db::{DbHandle, LockType};
|
use patch_db::{DbHandle, LockType};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::context::RpcContext;
|
|
||||||
use crate::dependencies::{break_transitive, heal_transitive, DependencyError};
|
use crate::dependencies::{break_transitive, heal_transitive, DependencyError};
|
||||||
use crate::s9pk::manifest::PackageId;
|
use crate::s9pk::manifest::PackageId;
|
||||||
use crate::status::health_check::{HealthCheckId, HealthCheckResult};
|
use crate::status::health_check::{HealthCheckId, HealthCheckResult};
|
||||||
use crate::status::MainStatus;
|
use crate::status::MainStatus;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
use crate::{context::RpcContext, dependencies::BreakTransitiveReceipts};
|
||||||
|
|
||||||
#[instrument(skip(ctx, db))]
|
#[instrument(skip(ctx, db))]
|
||||||
pub async fn check<Db: DbHandle>(
|
pub async fn check<Db: DbHandle>(
|
||||||
@@ -98,6 +98,10 @@ pub async fn check<Db: DbHandle>(
|
|||||||
|
|
||||||
checkpoint.save().await?;
|
checkpoint.save().await?;
|
||||||
|
|
||||||
|
tracing::debug!("Checking health of {}", id);
|
||||||
|
let receipts = crate::dependencies::BreakTransitiveReceipts::new(&mut tx).await?;
|
||||||
|
tracing::debug!("Got receipts {}", id);
|
||||||
|
|
||||||
for (dependent, info) in &*current_dependents {
|
for (dependent, info) in &*current_dependents {
|
||||||
let failures: BTreeMap<HealthCheckId, HealthCheckResult> = health_results
|
let failures: BTreeMap<HealthCheckId, HealthCheckResult> = health_results
|
||||||
.iter()
|
.iter()
|
||||||
@@ -113,10 +117,11 @@ pub async fn check<Db: DbHandle>(
|
|||||||
id,
|
id,
|
||||||
DependencyError::HealthChecksFailed { failures },
|
DependencyError::HealthChecksFailed { failures },
|
||||||
&mut BTreeMap::new(),
|
&mut BTreeMap::new(),
|
||||||
|
&receipts,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
heal_transitive(ctx, &mut tx, &dependent, id).await?;
|
heal_transitive(ctx, &mut tx, &dependent, id, &receipts.dependency_receipt).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ pub const SIG_CONTEXT: &'static [u8] = b"s9pk";
|
|||||||
#[instrument(skip(ctx))]
|
#[instrument(skip(ctx))]
|
||||||
pub fn pack(#[context] ctx: SdkContext, #[arg] path: Option<PathBuf>) -> Result<(), Error> {
|
pub fn pack(#[context] ctx: SdkContext, #[arg] path: Option<PathBuf>) -> Result<(), Error> {
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
|
||||||
|
|
||||||
let path = if let Some(path) = path {
|
let path = if let Some(path) = path {
|
||||||
path
|
path
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use tracing::metadata::LevelFilter;
|
|
||||||
use tracing::Subscriber;
|
use tracing::Subscriber;
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ impl Version {
|
|||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
self.string.as_str()
|
self.string.as_str()
|
||||||
}
|
}
|
||||||
|
pub fn into_version(self) -> emver::Version {
|
||||||
|
self.version
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for Version {
|
impl std::fmt::Display for Version {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ use std::cmp::Ordering;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use patch_db::json_ptr::JsonPointer;
|
use patch_db::DbHandle;
|
||||||
use patch_db::{DbHandle, LockType};
|
|
||||||
use rpc_toolkit::command;
|
use rpc_toolkit::command;
|
||||||
|
|
||||||
use crate::{Error, ResultExt};
|
use crate::{init::InitReceipts, Error};
|
||||||
|
|
||||||
mod v0_3_0;
|
mod v0_3_0;
|
||||||
mod v0_3_0_1;
|
mod v0_3_0_1;
|
||||||
@@ -15,7 +14,7 @@ mod v0_3_0_3;
|
|||||||
|
|
||||||
pub type Current = v0_3_0_3::Version;
|
pub type Current = v0_3_0_3::Version;
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
enum Version {
|
enum Version {
|
||||||
V0_3_0(Wrapper<v0_3_0::Version>),
|
V0_3_0(Wrapper<v0_3_0::Version>),
|
||||||
@@ -25,6 +24,27 @@ enum Version {
|
|||||||
Other(emver::Version),
|
Other(emver::Version),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Version {
|
||||||
|
fn from_util_version(version: crate::util::Version) -> Self {
|
||||||
|
serde_json::to_value(version.clone())
|
||||||
|
.and_then(serde_json::from_value)
|
||||||
|
.unwrap_or_else(|_e| {
|
||||||
|
tracing::warn!("Can't deserialize: {:?} and falling back to other", version);
|
||||||
|
Version::Other(version.into_version())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
fn as_sem_ver(&self) -> emver::Version {
|
||||||
|
match self {
|
||||||
|
Version::V0_3_0(Wrapper(x)) => x.semver(),
|
||||||
|
Version::V0_3_0_1(Wrapper(x)) => x.semver(),
|
||||||
|
Version::V0_3_0_2(Wrapper(x)) => x.semver(),
|
||||||
|
Version::V0_3_0_3(Wrapper(x)) => x.semver(),
|
||||||
|
Version::Other(x) => x.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait VersionT
|
pub trait VersionT
|
||||||
where
|
where
|
||||||
@@ -36,16 +56,18 @@ where
|
|||||||
fn compat(&self) -> &'static emver::VersionRange;
|
fn compat(&self) -> &'static emver::VersionRange;
|
||||||
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error>;
|
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error>;
|
||||||
async fn down<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error>;
|
async fn down<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error>;
|
||||||
async fn commit<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
|
async fn commit<Db: DbHandle>(
|
||||||
crate::db::DatabaseModel::new()
|
&self,
|
||||||
.server_info()
|
db: &mut Db,
|
||||||
.eos_version_compat()
|
receipts: &InitReceipts,
|
||||||
.put(db, &self.compat())
|
) -> Result<(), Error> {
|
||||||
|
receipts
|
||||||
|
.version_range
|
||||||
|
.set(db, self.compat().clone())
|
||||||
.await?;
|
.await?;
|
||||||
crate::db::DatabaseModel::new()
|
receipts
|
||||||
.server_info()
|
.server_version
|
||||||
.version()
|
.set(db, self.semver().into())
|
||||||
.put(db, &self.semver().into())
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -54,10 +76,11 @@ where
|
|||||||
&self,
|
&self,
|
||||||
version: &V,
|
version: &V,
|
||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
|
receipts: &InitReceipts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match self.semver().cmp(&version.semver()) {
|
match self.semver().cmp(&version.semver()) {
|
||||||
Ordering::Greater => self.rollback_to_unchecked(version, db).await,
|
Ordering::Greater => self.rollback_to_unchecked(version, db, receipts).await,
|
||||||
Ordering::Less => version.migrate_from_unchecked(self, db).await,
|
Ordering::Less => version.migrate_from_unchecked(self, db, receipts).await,
|
||||||
Ordering::Equal => Ok(()),
|
Ordering::Equal => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,31 +88,38 @@ where
|
|||||||
&self,
|
&self,
|
||||||
version: &V,
|
version: &V,
|
||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
|
receipts: &InitReceipts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let previous = Self::Previous::new();
|
let previous = Self::Previous::new();
|
||||||
if version.semver() != previous.semver() {
|
if version.semver() != previous.semver() {
|
||||||
previous.migrate_from_unchecked(version, db).await?;
|
previous
|
||||||
|
.migrate_from_unchecked(version, db, receipts)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
tracing::info!("{} -> {}", previous.semver(), self.semver(),);
|
tracing::info!("{} -> {}", previous.semver(), self.semver(),);
|
||||||
self.up(db).await?;
|
self.up(db).await?;
|
||||||
self.commit(db).await?;
|
self.commit(db, receipts).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn rollback_to_unchecked<V: VersionT, Db: DbHandle>(
|
async fn rollback_to_unchecked<V: VersionT, Db: DbHandle>(
|
||||||
&self,
|
&self,
|
||||||
version: &V,
|
version: &V,
|
||||||
db: &mut Db,
|
db: &mut Db,
|
||||||
|
receipts: &InitReceipts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let previous = Self::Previous::new();
|
let previous = Self::Previous::new();
|
||||||
tracing::info!("{} -> {}", self.semver(), previous.semver(),);
|
tracing::info!("{} -> {}", self.semver(), previous.semver(),);
|
||||||
self.down(db).await?;
|
self.down(db).await?;
|
||||||
previous.commit(db).await?;
|
previous.commit(db, receipts).await?;
|
||||||
if version.semver() != previous.semver() {
|
if version.semver() != previous.semver() {
|
||||||
previous.rollback_to_unchecked(version, db).await?;
|
previous
|
||||||
|
.rollback_to_unchecked(version, db, receipts)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
struct Wrapper<T>(T);
|
struct Wrapper<T>(T);
|
||||||
impl<T> serde::Serialize for Wrapper<T>
|
impl<T> serde::Serialize for Wrapper<T>
|
||||||
where
|
where
|
||||||
@@ -106,7 +136,7 @@ where
|
|||||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
let v = crate::util::Version::deserialize(deserializer)?;
|
let v = crate::util::Version::deserialize(deserializer)?;
|
||||||
let version = T::new();
|
let version = T::new();
|
||||||
if &*v == &version.semver() {
|
if *v == version.semver() {
|
||||||
Ok(Wrapper(version))
|
Ok(Wrapper(version))
|
||||||
} else {
|
} else {
|
||||||
Err(serde::de::Error::custom("Mismatched Version"))
|
Err(serde::de::Error::custom("Mismatched Version"))
|
||||||
@@ -114,17 +144,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init<Db: DbHandle>(db: &mut Db) -> Result<(), Error> {
|
pub async fn init<Db: DbHandle>(
|
||||||
let ptr: JsonPointer = "/server-info/version"
|
db: &mut Db,
|
||||||
.parse()
|
receipts: &crate::init::InitReceipts,
|
||||||
.with_kind(crate::ErrorKind::Database)?;
|
) -> Result<(), Error> {
|
||||||
db.lock(ptr.clone(), LockType::Write).await?;
|
let version = Version::from_util_version(receipts.server_version.get(db).await?);
|
||||||
let version: Version = db.get(&ptr).await?;
|
|
||||||
match version {
|
match version {
|
||||||
Version::V0_3_0(v) => v.0.migrate_to(&Current::new(), db).await?,
|
Version::V0_3_0(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
|
||||||
Version::V0_3_0_1(v) => v.0.migrate_to(&Current::new(), db).await?,
|
Version::V0_3_0_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
|
||||||
Version::V0_3_0_2(v) => v.0.migrate_to(&Current::new(), db).await?,
|
Version::V0_3_0_2(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
|
||||||
Version::V0_3_0_3(v) => v.0.migrate_to(&Current::new(), db).await?,
|
Version::V0_3_0_3(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
|
||||||
Version::Other(_) => {
|
Version::Other(_) => {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
eyre!("Cannot downgrade"),
|
eyre!("Cannot downgrade"),
|
||||||
@@ -135,10 +164,47 @@ pub async fn init<Db: DbHandle>(db: &mut Db) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const COMMIT_HASH: &'static str =
|
pub const COMMIT_HASH: &str =
|
||||||
git_version::git_version!(args = ["--always", "--abbrev=40", "--dirty=-modified"]);
|
git_version::git_version!(args = ["--always", "--abbrev=40", "--dirty=-modified"]);
|
||||||
|
|
||||||
#[command(rename = "git-info", local, metadata(authenticated = false))]
|
#[command(rename = "git-info", local, metadata(authenticated = false))]
|
||||||
pub fn git_info() -> Result<&'static str, Error> {
|
pub fn git_info() -> Result<&'static str, Error> {
|
||||||
Ok(COMMIT_HASH)
|
Ok(COMMIT_HASH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use proptest::prelude::*;
|
||||||
|
|
||||||
|
fn em_version() -> impl Strategy<Value = emver::Version> {
|
||||||
|
any::<(usize, usize, usize, usize)>().prop_map(|(major, minor, patch, super_minor)| {
|
||||||
|
emver::Version::new(major, minor, patch, super_minor)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn versions() -> impl Strategy<Value = Version> {
|
||||||
|
prop_oneof![
|
||||||
|
Just(Version::V0_3_0(Wrapper(v0_3_0::Version::new()))),
|
||||||
|
Just(Version::V0_3_0_1(Wrapper(v0_3_0_1::Version::new()))),
|
||||||
|
Just(Version::V0_3_0_2(Wrapper(v0_3_0_2::Version::new()))),
|
||||||
|
Just(Version::V0_3_0_3(Wrapper(v0_3_0_3::Version::new()))),
|
||||||
|
em_version().prop_map(Version::Other),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#[test]
|
||||||
|
fn emversion_isomorphic_version(original in em_version()) {
|
||||||
|
let version = Version::from_util_version(original.clone().into());
|
||||||
|
let back = version.as_sem_ver();
|
||||||
|
prop_assert_eq!(original, back, "All versions should round trip");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn version_isomorphic_em_version(version in versions()) {
|
||||||
|
let sem_ver = version.as_sem_ver();
|
||||||
|
let back = Version::from_util_version(sem_ver.into());
|
||||||
|
prop_assert_eq!(format!("{:?}",version), format!("{:?}", back), "All versions should round trip");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ lazy_static! {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Version;
|
pub struct Version;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl VersionT for Version {
|
impl VersionT for Version {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::util::Invoke;
|
|||||||
|
|
||||||
const V0_3_0_1: emver::Version = emver::Version::new(0, 3, 0, 1);
|
const V0_3_0_1: emver::Version = emver::Version::new(0, 3, 0, 1);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Version;
|
pub struct Version;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl VersionT for Version {
|
impl VersionT for Version {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use super::*;
|
|||||||
|
|
||||||
const V0_3_0_2: emver::Version = emver::Version::new(0, 3, 0, 2);
|
const V0_3_0_2: emver::Version = emver::Version::new(0, 3, 0, 2);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Version;
|
pub struct Version;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl VersionT for Version {
|
impl VersionT for Version {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use super::*;
|
|||||||
|
|
||||||
const V0_3_0_3: emver::Version = emver::Version::new(0, 3, 0, 3);
|
const V0_3_0_3: emver::Version = emver::Version::new(0, 3, 0, 3);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct Version;
|
pub struct Version;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl VersionT for Version {
|
impl VersionT for Version {
|
||||||
|
|||||||
274
frontend/package-lock.json
generated
274
frontend/package-lock.json
generated
@@ -318,6 +318,55 @@
|
|||||||
"yarn": ">= 1.13.0"
|
"yarn": ">= 1.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": {
|
||||||
|
"version": "13.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.0.tgz",
|
||||||
|
"integrity": "sha512-8YrreVbWlJVZnk5zs4vfkRItrPEtWhUcxWOBfYT/Kwu4FwJVAnNuhJAxxXOAQ2Ckd7cv30Idh/RFVLbTZ5Gs9w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ajv": "8.9.0",
|
||||||
|
"ajv-formats": "2.1.1",
|
||||||
|
"fast-json-stable-stringify": "2.1.0",
|
||||||
|
"magic-string": "0.25.7",
|
||||||
|
"rxjs": "6.6.7",
|
||||||
|
"source-map": "0.7.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.15.0 || >=16.10.0",
|
||||||
|
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
||||||
|
"yarn": ">= 1.13.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"chokidar": "^3.5.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"chokidar": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular-devkit/schematics/node_modules/ajv": {
|
||||||
|
"version": "8.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
|
||||||
|
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular-devkit/schematics/node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@angular/animations": {
|
"node_modules/@angular/animations": {
|
||||||
"version": "13.3.0",
|
"version": "13.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.3.0.tgz",
|
||||||
@@ -368,6 +417,70 @@
|
|||||||
"yarn": ">= 1.13.0"
|
"yarn": ">= 1.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/cli/node_modules/@angular-devkit/architect": {
|
||||||
|
"version": "0.1303.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.0.tgz",
|
||||||
|
"integrity": "sha512-kTcKB917ICA8j53SGo4gn+qAlzx8si+iHnOTbp5QlMr7qt/Iz07SVVI8mRlMD6c6lr7eE/fVlCLzEZ1+WCQpTA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular-devkit/core": "13.3.0",
|
||||||
|
"rxjs": "6.6.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.15.0 || >=16.10.0",
|
||||||
|
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
||||||
|
"yarn": ">= 1.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/cli/node_modules/@angular-devkit/core": {
|
||||||
|
"version": "13.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.0.tgz",
|
||||||
|
"integrity": "sha512-8YrreVbWlJVZnk5zs4vfkRItrPEtWhUcxWOBfYT/Kwu4FwJVAnNuhJAxxXOAQ2Ckd7cv30Idh/RFVLbTZ5Gs9w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ajv": "8.9.0",
|
||||||
|
"ajv-formats": "2.1.1",
|
||||||
|
"fast-json-stable-stringify": "2.1.0",
|
||||||
|
"magic-string": "0.25.7",
|
||||||
|
"rxjs": "6.6.7",
|
||||||
|
"source-map": "0.7.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.15.0 || >=16.10.0",
|
||||||
|
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
||||||
|
"yarn": ">= 1.13.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"chokidar": "^3.5.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"chokidar": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/cli/node_modules/ajv": {
|
||||||
|
"version": "8.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
|
||||||
|
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular/cli/node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@angular/common": {
|
"node_modules/@angular/common": {
|
||||||
"version": "13.3.0",
|
"version": "13.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/common/-/common-13.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/common/-/common-13.3.0.tgz",
|
||||||
@@ -3132,6 +3245,55 @@
|
|||||||
"yarn": ">= 1.13.0"
|
"yarn": ">= 1.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@schematics/angular/node_modules/@angular-devkit/core": {
|
||||||
|
"version": "13.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.0.tgz",
|
||||||
|
"integrity": "sha512-8YrreVbWlJVZnk5zs4vfkRItrPEtWhUcxWOBfYT/Kwu4FwJVAnNuhJAxxXOAQ2Ckd7cv30Idh/RFVLbTZ5Gs9w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ajv": "8.9.0",
|
||||||
|
"ajv-formats": "2.1.1",
|
||||||
|
"fast-json-stable-stringify": "2.1.0",
|
||||||
|
"magic-string": "0.25.7",
|
||||||
|
"rxjs": "6.6.7",
|
||||||
|
"source-map": "0.7.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.15.0 || >=16.10.0",
|
||||||
|
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
||||||
|
"yarn": ">= 1.13.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"chokidar": "^3.5.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"chokidar": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@schematics/angular/node_modules/ajv": {
|
||||||
|
"version": "8.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
|
||||||
|
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@schematics/angular/node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@start9labs/argon2": {
|
"node_modules/@start9labs/argon2": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@start9labs/argon2/-/argon2-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@start9labs/argon2/-/argon2-0.1.0.tgz",
|
||||||
@@ -14282,6 +14444,40 @@
|
|||||||
"magic-string": "0.25.7",
|
"magic-string": "0.25.7",
|
||||||
"ora": "5.4.1",
|
"ora": "5.4.1",
|
||||||
"rxjs": "6.6.7"
|
"rxjs": "6.6.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@angular-devkit/core": {
|
||||||
|
"version": "13.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.0.tgz",
|
||||||
|
"integrity": "sha512-8YrreVbWlJVZnk5zs4vfkRItrPEtWhUcxWOBfYT/Kwu4FwJVAnNuhJAxxXOAQ2Ckd7cv30Idh/RFVLbTZ5Gs9w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ajv": "8.9.0",
|
||||||
|
"ajv-formats": "2.1.1",
|
||||||
|
"fast-json-stable-stringify": "2.1.0",
|
||||||
|
"magic-string": "0.25.7",
|
||||||
|
"rxjs": "6.6.7",
|
||||||
|
"source-map": "0.7.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ajv": {
|
||||||
|
"version": "8.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
|
||||||
|
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/animations": {
|
"@angular/animations": {
|
||||||
@@ -14317,6 +14513,50 @@
|
|||||||
"semver": "7.3.5",
|
"semver": "7.3.5",
|
||||||
"symbol-observable": "4.0.0",
|
"symbol-observable": "4.0.0",
|
||||||
"uuid": "8.3.2"
|
"uuid": "8.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@angular-devkit/architect": {
|
||||||
|
"version": "0.1303.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.0.tgz",
|
||||||
|
"integrity": "sha512-kTcKB917ICA8j53SGo4gn+qAlzx8si+iHnOTbp5QlMr7qt/Iz07SVVI8mRlMD6c6lr7eE/fVlCLzEZ1+WCQpTA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@angular-devkit/core": "13.3.0",
|
||||||
|
"rxjs": "6.6.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular-devkit/core": {
|
||||||
|
"version": "13.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.0.tgz",
|
||||||
|
"integrity": "sha512-8YrreVbWlJVZnk5zs4vfkRItrPEtWhUcxWOBfYT/Kwu4FwJVAnNuhJAxxXOAQ2Ckd7cv30Idh/RFVLbTZ5Gs9w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ajv": "8.9.0",
|
||||||
|
"ajv-formats": "2.1.1",
|
||||||
|
"fast-json-stable-stringify": "2.1.0",
|
||||||
|
"magic-string": "0.25.7",
|
||||||
|
"rxjs": "6.6.7",
|
||||||
|
"source-map": "0.7.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ajv": {
|
||||||
|
"version": "8.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
|
||||||
|
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/common": {
|
"@angular/common": {
|
||||||
@@ -16309,6 +16549,40 @@
|
|||||||
"@angular-devkit/core": "13.3.0",
|
"@angular-devkit/core": "13.3.0",
|
||||||
"@angular-devkit/schematics": "13.3.0",
|
"@angular-devkit/schematics": "13.3.0",
|
||||||
"jsonc-parser": "3.0.0"
|
"jsonc-parser": "3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@angular-devkit/core": {
|
||||||
|
"version": "13.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.0.tgz",
|
||||||
|
"integrity": "sha512-8YrreVbWlJVZnk5zs4vfkRItrPEtWhUcxWOBfYT/Kwu4FwJVAnNuhJAxxXOAQ2Ckd7cv30Idh/RFVLbTZ5Gs9w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ajv": "8.9.0",
|
||||||
|
"ajv-formats": "2.1.1",
|
||||||
|
"fast-json-stable-stringify": "2.1.0",
|
||||||
|
"magic-string": "0.25.7",
|
||||||
|
"rxjs": "6.6.7",
|
||||||
|
"source-map": "0.7.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ajv": {
|
||||||
|
"version": "8.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
|
||||||
|
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@start9labs/argon2": {
|
"@start9labs/argon2": {
|
||||||
|
|||||||
2
patch-db
2
patch-db
Submodule patch-db updated: d3426671a1...35973d7aef
Reference in New Issue
Block a user