mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +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:
@@ -2,11 +2,10 @@ use std::cmp::Ordering;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use color_eyre::eyre::eyre;
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
use patch_db::{DbHandle, LockType};
|
||||
use patch_db::DbHandle;
|
||||
use rpc_toolkit::command;
|
||||
|
||||
use crate::{Error, ResultExt};
|
||||
use crate::{init::InitReceipts, Error};
|
||||
|
||||
mod v0_3_0;
|
||||
mod v0_3_0_1;
|
||||
@@ -15,7 +14,7 @@ mod v0_3_0_3;
|
||||
|
||||
pub type Current = v0_3_0_3::Version;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
#[serde(untagged)]
|
||||
enum Version {
|
||||
V0_3_0(Wrapper<v0_3_0::Version>),
|
||||
@@ -25,6 +24,27 @@ enum 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]
|
||||
pub trait VersionT
|
||||
where
|
||||
@@ -36,16 +56,18 @@ where
|
||||
fn compat(&self) -> &'static emver::VersionRange;
|
||||
async fn up<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> {
|
||||
crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.eos_version_compat()
|
||||
.put(db, &self.compat())
|
||||
async fn commit<Db: DbHandle>(
|
||||
&self,
|
||||
db: &mut Db,
|
||||
receipts: &InitReceipts,
|
||||
) -> Result<(), Error> {
|
||||
receipts
|
||||
.version_range
|
||||
.set(db, self.compat().clone())
|
||||
.await?;
|
||||
crate::db::DatabaseModel::new()
|
||||
.server_info()
|
||||
.version()
|
||||
.put(db, &self.semver().into())
|
||||
receipts
|
||||
.server_version
|
||||
.set(db, self.semver().into())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@@ -54,10 +76,11 @@ where
|
||||
&self,
|
||||
version: &V,
|
||||
db: &mut Db,
|
||||
receipts: &InitReceipts,
|
||||
) -> Result<(), Error> {
|
||||
match self.semver().cmp(&version.semver()) {
|
||||
Ordering::Greater => self.rollback_to_unchecked(version, db).await,
|
||||
Ordering::Less => version.migrate_from_unchecked(self, db).await,
|
||||
Ordering::Greater => self.rollback_to_unchecked(version, db, receipts).await,
|
||||
Ordering::Less => version.migrate_from_unchecked(self, db, receipts).await,
|
||||
Ordering::Equal => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -65,31 +88,38 @@ where
|
||||
&self,
|
||||
version: &V,
|
||||
db: &mut Db,
|
||||
receipts: &InitReceipts,
|
||||
) -> Result<(), Error> {
|
||||
let previous = Self::Previous::new();
|
||||
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(),);
|
||||
self.up(db).await?;
|
||||
self.commit(db).await?;
|
||||
self.commit(db, receipts).await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn rollback_to_unchecked<V: VersionT, Db: DbHandle>(
|
||||
&self,
|
||||
version: &V,
|
||||
db: &mut Db,
|
||||
receipts: &InitReceipts,
|
||||
) -> Result<(), Error> {
|
||||
let previous = Self::Previous::new();
|
||||
tracing::info!("{} -> {}", self.semver(), previous.semver(),);
|
||||
self.down(db).await?;
|
||||
previous.commit(db).await?;
|
||||
previous.commit(db, receipts).await?;
|
||||
if version.semver() != previous.semver() {
|
||||
previous.rollback_to_unchecked(version, db).await?;
|
||||
previous
|
||||
.rollback_to_unchecked(version, db, receipts)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
struct Wrapper<T>(T);
|
||||
impl<T> serde::Serialize for Wrapper<T>
|
||||
where
|
||||
@@ -106,7 +136,7 @@ where
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let v = crate::util::Version::deserialize(deserializer)?;
|
||||
let version = T::new();
|
||||
if &*v == &version.semver() {
|
||||
if *v == version.semver() {
|
||||
Ok(Wrapper(version))
|
||||
} else {
|
||||
Err(serde::de::Error::custom("Mismatched Version"))
|
||||
@@ -114,17 +144,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn init<Db: DbHandle>(db: &mut Db) -> Result<(), Error> {
|
||||
let ptr: JsonPointer = "/server-info/version"
|
||||
.parse()
|
||||
.with_kind(crate::ErrorKind::Database)?;
|
||||
db.lock(ptr.clone(), LockType::Write).await?;
|
||||
let version: Version = db.get(&ptr).await?;
|
||||
pub async fn init<Db: DbHandle>(
|
||||
db: &mut Db,
|
||||
receipts: &crate::init::InitReceipts,
|
||||
) -> Result<(), Error> {
|
||||
let version = Version::from_util_version(receipts.server_version.get(db).await?);
|
||||
match version {
|
||||
Version::V0_3_0(v) => v.0.migrate_to(&Current::new(), db).await?,
|
||||
Version::V0_3_0_1(v) => v.0.migrate_to(&Current::new(), db).await?,
|
||||
Version::V0_3_0_2(v) => v.0.migrate_to(&Current::new(), db).await?,
|
||||
Version::V0_3_0_3(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, receipts).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, receipts).await?,
|
||||
Version::Other(_) => {
|
||||
return Err(Error::new(
|
||||
eyre!("Cannot downgrade"),
|
||||
@@ -135,10 +164,47 @@ pub async fn init<Db: DbHandle>(db: &mut Db) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub const COMMIT_HASH: &'static str =
|
||||
pub const COMMIT_HASH: &str =
|
||||
git_version::git_version!(args = ["--always", "--abbrev=40", "--dirty=-modified"]);
|
||||
|
||||
#[command(rename = "git-info", local, metadata(authenticated = false))]
|
||||
pub fn git_info() -> Result<&'static str, Error> {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user