feat: Change all the dependency errors at once (#2427)

* feat: Change all the dependency errors at once

* remove deprecated dependency-errors field

* set pointers to [] by default

* chore: Something about fixing the build

* fix migration

---------

Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
J H
2023-09-29 12:08:53 -06:00
committed by GitHub
parent e6e4cd63f3
commit 5e3412d735
12 changed files with 170 additions and 34 deletions

4
backend/Cargo.lock generated
View File

@@ -2120,9 +2120,9 @@ dependencies = [
[[package]] [[package]]
name = "indicatif" name = "indicatif"
version = "0.17.6" version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730" checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
dependencies = [ dependencies = [
"console", "console",
"instant", "instant",

View File

@@ -22,6 +22,7 @@ use crate::account::AccountInfo;
use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation}; use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation};
use crate::db::model::{CurrentDependents, Database, PackageDataEntryMatchModelRef}; use crate::db::model::{CurrentDependents, Database, PackageDataEntryMatchModelRef};
use crate::db::prelude::PatchDbExt; use crate::db::prelude::PatchDbExt;
use crate::dependencies::compute_dependency_config_errs;
use crate::disk::OsPartitionInfo; use crate::disk::OsPartitionInfo;
use crate::init::init_postgres; use crate::init::init_postgres;
use crate::install::cleanup::{cleanup_failed, uninstall}; use crate::install::cleanup::{cleanup_failed, uninstall};
@@ -155,8 +156,7 @@ impl RpcContext {
.unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))),
tor_proxy, tor_proxy,
base.dns_bind base.dns_bind
.as_ref() .as_deref()
.map(|v| v.as_slice())
.unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]), .unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]),
SslManager::new(&account)?, SslManager::new(&account)?,
&account.hostname, &account.hostname,
@@ -217,11 +217,8 @@ impl RpcContext {
}); });
let res = Self(seed.clone()); let res = Self(seed.clone());
res.cleanup().await?; res.cleanup_and_initialize().await?;
tracing::info!("Cleaned up transient states"); tracing::info!("Cleaned up transient states");
let peeked = res.db.peek().await?;
res.managers.init(res.clone(), peeked).await?;
tracing::info!("Initialized Package Managers");
Ok(res) Ok(res)
} }
@@ -236,7 +233,7 @@ impl RpcContext {
} }
#[instrument(skip(self))] #[instrument(skip(self))]
pub async fn cleanup(&self) -> Result<(), Error> { pub async fn cleanup_and_initialize(&self) -> Result<(), Error> {
self.db self.db
.mutate(|f| { .mutate(|f| {
let mut current_dependents = f let mut current_dependents = f
@@ -278,9 +275,10 @@ impl RpcContext {
Ok(()) Ok(())
}) })
.await?; .await?;
let peek = self.db.peek().await?; let peek = self.db.peek().await?;
for (package_id, package) in peek.as_package_data().as_entries()?.into_iter() { for (package_id, package) in peek.as_package_data().as_entries()?.into_iter() {
let package = package.clone();
let action = match package.as_match() { let action = match package.as_match() {
PackageDataEntryMatchModelRef::Installing(_) PackageDataEntryMatchModelRef::Installing(_)
| PackageDataEntryMatchModelRef::Restoring(_) | PackageDataEntryMatchModelRef::Restoring(_)
@@ -298,7 +296,7 @@ impl RpcContext {
&self.datadir, &self.datadir,
&package_id, &package_id,
&version, &version,
&volume_id, volume_id,
)) ))
.with_kind(ErrorKind::Filesystem)?; .with_kind(ErrorKind::Filesystem)?;
if tokio::fs::metadata(&tmp_path).await.is_ok() { if tokio::fs::metadata(&tmp_path).await.is_ok() {
@@ -314,7 +312,8 @@ impl RpcContext {
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
self.db let peek = self
.db
.mutate(|v| { .mutate(|v| {
for (_, pde) in v.as_package_data_mut().as_entries_mut()? { for (_, pde) in v.as_package_data_mut().as_entries_mut()? {
let status = pde let status = pde
@@ -329,9 +328,49 @@ impl RpcContext {
MainStatus::Stopped MainStatus::Stopped
})?; })?;
} }
Ok(v.clone())
})
.await?;
self.managers.init(self.clone(), peek.clone()).await?;
tracing::info!("Initialized Package Managers");
let mut all_dependency_config_errs = BTreeMap::new();
for (package_id, package) in peek.as_package_data().as_entries()?.into_iter() {
let package = package.clone();
if let Some(current_dependencies) = package
.as_installed()
.and_then(|x| x.as_current_dependencies().de().ok())
{
let manifest = package.as_manifest().de()?;
all_dependency_config_errs.insert(
package_id.clone(),
compute_dependency_config_errs(
self,
&peek,
&manifest,
&current_dependencies,
&Default::default(),
)
.await?,
);
}
}
self.db
.mutate(|v| {
for (package_id, errs) in all_dependency_config_errs {
if let Some(config_errors) = v
.as_package_data_mut()
.as_idx_mut(&package_id)
.and_then(|pde| pde.as_installed_mut())
.map(|i| i.as_status_mut().as_dependency_config_errors_mut())
{
config_errors.ser(&errs)?;
}
}
Ok(()) Ok(())
}) })
.await?; .await?;
Ok(()) Ok(())
} }
@@ -389,7 +428,7 @@ impl RpcContext {
} }
impl AsRef<Jwk> for RpcContext { impl AsRef<Jwk> for RpcContext {
fn as_ref(&self) -> &Jwk { fn as_ref(&self) -> &Jwk {
&*CURRENT_SECRET &CURRENT_SECRET
} }
} }
impl Context for RpcContext {} impl Context for RpcContext {}
@@ -403,7 +442,7 @@ impl Deref for RpcContext {
tracing_error::SpanTrace::capture() tracing_error::SpanTrace::capture()
); );
} }
&*self.0 &self.0
} }
} }
impl Drop for RpcContext { impl Drop for RpcContext {

View File

@@ -480,6 +480,7 @@ pub struct StaticDependencyInfo {
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
#[model = "Model<Self>"] #[model = "Model<Self>"]
pub struct CurrentDependencyInfo { pub struct CurrentDependencyInfo {
#[serde(default)]
pub pointers: BTreeSet<PackagePointerSpec>, pub pointers: BTreeSet<PackagePointerSpec>,
pub health_checks: BTreeSet<HealthCheckId>, pub health_checks: BTreeSet<HealthCheckId>,
} }

View File

@@ -200,11 +200,6 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
let account = AccountInfo::load(&secret_store).await?; let account = AccountInfo::load(&secret_store).await?;
let db = cfg.db(&account).await?; let db = cfg.db(&account).await?;
db.mutate(|d| {
let model = d.de()?;
d.ser(&model)
})
.await?;
tracing::info!("Opened PatchDB"); tracing::info!("Opened PatchDB");
let peek = db.peek().await?; let peek = db.peek().await?;
let mut server_info = peek.as_server_info().de()?; let mut server_info = peek.as_server_info().de()?;
@@ -375,6 +370,12 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
crate::version::init(&db, &secret_store).await?; crate::version::init(&db, &secret_store).await?;
db.mutate(|d| {
let model = d.de()?;
d.ser(&model)
})
.await?;
if should_rebuild { if should_rebuild {
match tokio::fs::remove_file(SYSTEM_REBUILD_PATH).await { match tokio::fs::remove_file(SYSTEM_REBUILD_PATH).await {
Ok(()) => Ok(()), Ok(()) => Ok(()),

View File

@@ -1105,7 +1105,6 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
status: Status { status: Status {
configured: manifest.config.is_none(), configured: manifest.config.is_none(),
main: MainStatus::Stopped, main: MainStatus::Stopped,
dependency_errors: Default::default(),
dependency_config_errors: compute_dependency_config_errs( dependency_config_errors: compute_dependency_config_errs(
&ctx, &ctx,
&peek, &peek,

View File

@@ -16,8 +16,6 @@ pub struct Status {
pub configured: bool, pub configured: bool,
pub main: MainStatus, pub main: MainStatus,
#[serde(default)] #[serde(default)]
pub dependency_errors: BTreeMap<(), ()>, // TODO: remove
#[serde(default)]
pub dependency_config_errors: DependencyConfigErrors, pub dependency_config_errors: DependencyConfigErrors,
} }

View File

@@ -13,8 +13,9 @@ mod v0_3_4_1;
mod v0_3_4_2; mod v0_3_4_2;
mod v0_3_4_3; mod v0_3_4_3;
mod v0_3_4_4; mod v0_3_4_4;
mod v0_3_5;
pub type Current = v0_3_4_4::Version; pub type Current = v0_3_5::Version;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(untagged)] #[serde(untagged)]
@@ -24,6 +25,7 @@ enum Version {
V0_3_4_2(Wrapper<v0_3_4_2::Version>), V0_3_4_2(Wrapper<v0_3_4_2::Version>),
V0_3_4_3(Wrapper<v0_3_4_3::Version>), V0_3_4_3(Wrapper<v0_3_4_3::Version>),
V0_3_4_4(Wrapper<v0_3_4_4::Version>), V0_3_4_4(Wrapper<v0_3_4_4::Version>),
V0_3_5(Wrapper<v0_3_5::Version>),
Other(emver::Version), Other(emver::Version),
} }
@@ -44,6 +46,7 @@ impl Version {
Version::V0_3_4_2(Wrapper(x)) => x.semver(), Version::V0_3_4_2(Wrapper(x)) => x.semver(),
Version::V0_3_4_3(Wrapper(x)) => x.semver(), Version::V0_3_4_3(Wrapper(x)) => x.semver(),
Version::V0_3_4_4(Wrapper(x)) => x.semver(), Version::V0_3_4_4(Wrapper(x)) => x.semver(),
Version::V0_3_5(Wrapper(x)) => x.semver(),
Version::Other(x) => x.clone(), Version::Other(x) => x.clone(),
} }
} }
@@ -168,6 +171,7 @@ pub async fn init(db: &PatchDb, secrets: &PgPool) -> Result<(), Error> {
Version::V0_3_4_2(v) => v.0.migrate_to(&Current::new(), db.clone(), secrets).await?, Version::V0_3_4_2(v) => v.0.migrate_to(&Current::new(), db.clone(), secrets).await?,
Version::V0_3_4_3(v) => v.0.migrate_to(&Current::new(), db.clone(), secrets).await?, Version::V0_3_4_3(v) => v.0.migrate_to(&Current::new(), db.clone(), secrets).await?,
Version::V0_3_4_4(v) => v.0.migrate_to(&Current::new(), db.clone(), secrets).await?, Version::V0_3_4_4(v) => v.0.migrate_to(&Current::new(), db.clone(), secrets).await?,
Version::V0_3_5(v) => v.0.migrate_to(&Current::new(), db.clone(), secrets).await?,
Version::Other(_) => { Version::Other(_) => {
return Err(Error::new( return Err(Error::new(
eyre!("Cannot downgrade"), eyre!("Cannot downgrade"),

View File

@@ -0,0 +1,98 @@
use std::collections::BTreeMap;
use std::path::Path;
use async_trait::async_trait;
use emver::VersionRange;
use models::DataUrl;
use sqlx::PgPool;
use super::v0_3_4::V0_3_0_COMPAT;
use super::{v0_3_4_4, VersionT};
use crate::prelude::*;
const V0_3_5: emver::Version = emver::Version::new(0, 3, 5, 0);
#[derive(Clone, Debug)]
pub struct Version;
#[async_trait]
impl VersionT for Version {
type Previous = v0_3_4_4::Version;
fn new() -> Self {
Version
}
fn semver(&self) -> emver::Version {
V0_3_5
}
fn compat(&self) -> &'static VersionRange {
&V0_3_0_COMPAT
}
async fn up(&self, db: PatchDb, _secrets: &PgPool) -> Result<(), Error> {
let peek = db.peek().await?;
let mut url_replacements = BTreeMap::new();
for (_, pde) in peek.as_package_data().as_entries()? {
for (dependency, info) in pde
.as_installed()
.map(|i| i.as_dependency_info().as_entries())
.transpose()?
.into_iter()
.flatten()
{
if !url_replacements.contains_key(&dependency) {
url_replacements.insert(
dependency,
DataUrl::from_path(
<&Value>::from(info.as_icon())
.as_str()
.and_then(|i| i.strip_prefix("/public/package-data/"))
.map(|path| {
Path::new("/embassy-data/package-data/public").join(path)
})
.unwrap_or_default(),
)
.await
.unwrap_or_else(|_| {
DataUrl::from_slice(
"image/png",
include_bytes!("../install/package-icon.png"),
)
}),
);
}
}
}
db.mutate(|v| {
for (_, pde) in v.as_package_data_mut().as_entries_mut()? {
for (dependency, info) in pde
.as_installed_mut()
.map(|i| i.as_dependency_info_mut().as_entries_mut())
.transpose()?
.into_iter()
.flatten()
{
if let Some(url) = url_replacements.get(&dependency) {
info.as_icon_mut().ser(url)?;
}
let manifest = <&mut Value>::from(&mut *info)
.as_object_mut()
.and_then(|o| o.remove("manifest"));
if let Some(title) = manifest
.as_ref()
.and_then(|m| m.as_object())
.and_then(|m| m.get("title"))
.and_then(|t| t.as_str())
.map(|s| s.to_owned())
{
info.as_title_mut().ser(&title)?;
}
}
}
Ok(())
})
.await?;
Ok(())
}
async fn down(&self, _db: PatchDb, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -120,7 +120,7 @@ export class AppShowPage {
return { return {
id: depId, id: depId,
version: pkgInstalled.manifest.dependencies[depId].version, // do we want this version range? version: pkgInstalled.manifest.dependencies[depId].version, // do we want this version range?
title: depInfo?.manifest?.title || depId, title: depInfo?.title || depId,
icon: depInfo?.icon || '', icon: depInfo?.icon || '',
errorText: errorText errorText: errorText
? `${errorText}. ${pkgInstalled.manifest.title} will not work as expected.` ? `${errorText}. ${pkgInstalled.manifest.title} will not work as expected.`

View File

@@ -1958,7 +1958,7 @@ export module Mock {
}, },
'dependency-info': { 'dependency-info': {
bitcoind: { bitcoind: {
manifest: Mock.MockManifestBitcoind, title: Mock.MockManifestBitcoind.title,
icon: 'assets/img/service-icons/bitcoind.svg', icon: 'assets/img/service-icons/bitcoind.svg',
}, },
}, },
@@ -2012,11 +2012,11 @@ export module Mock {
}, },
'dependency-info': { 'dependency-info': {
bitcoind: { bitcoind: {
manifest: Mock.MockManifestBitcoind, title: Mock.MockManifestBitcoind.title,
icon: 'assets/img/service-icons/bitcoind.svg', icon: 'assets/img/service-icons/bitcoind.svg',
}, },
'btc-rpc-proxy': { 'btc-rpc-proxy': {
manifest: Mock.MockManifestBitcoinProxy, title: Mock.MockManifestBitcoinProxy.title,
icon: 'assets/img/service-icons/btc-rpc-proxy.png', icon: 'assets/img/service-icons/btc-rpc-proxy.png',
}, },
}, },

View File

@@ -664,15 +664,11 @@ export const mockPatchData: DataModel = {
}, },
'dependency-info': { 'dependency-info': {
bitcoind: { bitcoind: {
manifest: { title: 'Bitcoin Core',
title: 'Bitcoin Core',
} as Manifest,
icon: 'assets/img/service-icons/bitcoind.svg', icon: 'assets/img/service-icons/bitcoind.svg',
}, },
'btc-rpc-proxy': { 'btc-rpc-proxy': {
manifest: { title: 'Bitcoin Proxy',
title: 'Bitcoin Proxy',
} as Manifest,
icon: 'assets/img/service-icons/btc-rpc-proxy.png', icon: 'assets/img/service-icons/btc-rpc-proxy.png',
}, },
}, },

View File

@@ -132,7 +132,7 @@ export interface InstalledPackageDataEntry {
'current-dependencies': { [id: string]: CurrentDependencyInfo } 'current-dependencies': { [id: string]: CurrentDependencyInfo }
'dependency-info': { 'dependency-info': {
[id: string]: { [id: string]: {
manifest: Manifest title: string
icon: Url icon: Url
} }
} }