From 1a86f393d618dc2c2f58f9c324657865fe29aef5 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Mon, 27 Sep 2021 17:19:58 -0600 Subject: [PATCH] use btreemap instead of indexmap to establish canonical lock ordering --- appmgr/Cargo.lock | 59 ++++++++++++++++- appmgr/Cargo.toml | 1 + appmgr/src/action/docker.rs | 4 +- appmgr/src/action/mod.rs | 11 ++-- appmgr/src/auth.rs | 4 +- appmgr/src/config/action.rs | 5 +- appmgr/src/config/mod.rs | 19 +++--- appmgr/src/config/spec.rs | 101 ++++++++---------------------- appmgr/src/control.rs | 5 +- appmgr/src/db/model.rs | 14 ++--- appmgr/src/dependencies.rs | 11 ++-- appmgr/src/disk/util.rs | 7 ++- appmgr/src/id.rs | 4 +- appmgr/src/install/mod.rs | 11 ++-- appmgr/src/net/interface.rs | 14 ++--- appmgr/src/net/nginx.rs | 6 +- appmgr/src/s9pk/manifest.rs | 2 +- appmgr/src/status/health_check.rs | 8 +-- appmgr/src/status/mod.rs | 23 ++++--- appmgr/src/util/mod.rs | 65 ++++++++++++++++++- appmgr/src/volume.rs | 8 +-- patch-db | 2 +- 22 files changed, 228 insertions(+), 156 deletions(-) diff --git a/appmgr/Cargo.lock b/appmgr/Cargo.lock index 084d50f46..b52d2a2b0 100644 --- a/appmgr/Cargo.lock +++ b/appmgr/Cargo.lock @@ -788,6 +788,7 @@ dependencies = [ "libc", "log", "nix 0.22.1", + "num", "openssh-keys", "openssl", "patch-db", @@ -1728,6 +1729,40 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -1738,6 +1773,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.14" @@ -1868,7 +1926,6 @@ dependencies = [ "async-trait", "fd-lock-rs", "futures", - "indexmap", "json-patch", "json-ptr", "lazy_static", diff --git a/appmgr/Cargo.toml b/appmgr/Cargo.toml index 89e128052..da6210189 100644 --- a/appmgr/Cargo.toml +++ b/appmgr/Cargo.toml @@ -73,6 +73,7 @@ lazy_static = "1.4" libc = "0.2.103" log = "0.4.14" nix = "0.22.1" +num = "0.4.0" openssh-keys = "0.5.0" openssl = { version = "0.10.36", features = ["vendored"] } patch-db = { version = "*", path = "../patch-db/patch-db", features = ["log"] } diff --git a/appmgr/src/action/docker.rs b/appmgr/src/action/docker.rs index 1adaf9052..3c49acd95 100644 --- a/appmgr/src/action/docker.rs +++ b/appmgr/src/action/docker.rs @@ -1,9 +1,9 @@ use std::borrow::Cow; +use std::collections::BTreeMap; use std::ffi::{OsStr, OsString}; use std::net::Ipv4Addr; use std::path::PathBuf; -use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -26,7 +26,7 @@ pub struct DockerAction { #[serde(default)] pub args: Vec, #[serde(default)] - pub mounts: IndexMap, + pub mounts: BTreeMap, #[serde(default)] pub io_format: Option, #[serde(default)] diff --git a/appmgr/src/action/mod.rs b/appmgr/src/action/mod.rs index d4a1e375f..b11d1f960 100644 --- a/appmgr/src/action/mod.rs +++ b/appmgr/src/action/mod.rs @@ -1,9 +1,10 @@ +use std::collections::BTreeMap; use std::path::Path; use std::str::FromStr; use anyhow::anyhow; use clap::ArgMatches; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexSet; use patch_db::HasModel; use rpc_toolkit::command; use serde::{Deserialize, Serialize}; @@ -13,9 +14,7 @@ use crate::config::{Config, ConfigSpec}; use crate::context::RpcContext; use crate::id::{Id, InvalidId}; use crate::s9pk::manifest::PackageId; -use crate::util::{ - display_serializable, parse_stdin_deserializable, IoFormat, ValuePrimative, Version, -}; +use crate::util::{display_serializable, parse_stdin_deserializable, IoFormat, Version}; use crate::volume::Volumes; use crate::{Error, ResultExt}; @@ -23,7 +22,7 @@ pub mod docker; // TODO: create RPC endpoint that looks up the appropriate action and calls `execute` -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct ActionId = String>(Id); impl FromStr for ActionId { type Err = InvalidId; @@ -70,7 +69,7 @@ where } #[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct Actions(pub IndexMap); +pub struct Actions(pub BTreeMap); #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "version")] diff --git a/appmgr/src/auth.rs b/appmgr/src/auth.rs index 555b82b31..dba55d0a7 100644 --- a/appmgr/src/auth.rs +++ b/appmgr/src/auth.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::marker::PhantomData; use anyhow::anyhow; @@ -6,7 +7,6 @@ use chrono::{DateTime, Utc}; use clap::ArgMatches; use http::header::COOKIE; use http::HeaderValue; -use indexmap::IndexMap; use rpc_toolkit::command; use rpc_toolkit::command_helpers::prelude::{RequestParts, ResponseParts}; use rpc_toolkit::yajrc::RpcError; @@ -160,7 +160,7 @@ pub struct Session { #[serde(rename_all = "kebab-case")] pub struct SessionList { current: String, - sessions: IndexMap, + sessions: BTreeMap, } #[command(subcommands(list, kill))] diff --git a/appmgr/src/config/action.rs b/appmgr/src/config/action.rs index bd08fb974..096f77e32 100644 --- a/appmgr/src/config/action.rs +++ b/appmgr/src/config/action.rs @@ -1,5 +1,6 @@ +use std::collections::{BTreeMap, BTreeSet}; + use anyhow::anyhow; -use indexmap::{IndexMap, IndexSet}; use nix::sys::signal::Signal; use patch_db::HasModel; use serde::{Deserialize, Serialize}; @@ -93,5 +94,5 @@ pub struct SetResult { #[serde(deserialize_with = "crate::util::deserialize_from_str_opt")] #[serde(serialize_with = "crate::util::serialize_display_opt")] pub signal: Option, - pub depends_on: IndexMap>, + pub depends_on: BTreeMap>, } diff --git a/appmgr/src/config/mod.rs b/appmgr/src/config/mod.rs index c4c4462ea..590a2f799 100644 --- a/appmgr/src/config/mod.rs +++ b/appmgr/src/config/mod.rs @@ -1,9 +1,10 @@ +use std::collections::{BTreeMap, BTreeSet}; use std::time::Duration; use anyhow::anyhow; use bollard::container::KillContainerOptions; use futures::future::{BoxFuture, FutureExt}; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexSet; use itertools::Itertools; use patch_db::DbHandle; use rand::SeedableRng; @@ -205,7 +206,7 @@ pub async fn set_dry( ) -> Result { let mut db = ctx.db.handle(); let mut tx = db.begin().await?; - let mut breakages = IndexMap::new(); + let mut breakages = BTreeMap::new(); configure( &ctx, &mut tx, @@ -213,7 +214,7 @@ pub async fn set_dry( config, &timeout, true, - &mut IndexMap::new(), + &mut BTreeMap::new(), &mut breakages, ) .await?; @@ -241,7 +242,7 @@ pub async fn set_impl( ) -> Result, Error> { let mut db = ctx.db.handle(); let mut tx = db.begin().await?; - let mut breakages = IndexMap::new(); + let mut breakages = BTreeMap::new(); configure( &ctx, &mut tx, @@ -249,7 +250,7 @@ pub async fn set_impl( config, &timeout, false, - &mut IndexMap::new(), + &mut BTreeMap::new(), &mut breakages, ) .await?; @@ -278,8 +279,8 @@ pub fn configure<'a, Db: DbHandle>( config: Option, timeout: &'a Option, dry_run: bool, - overrides: &'a mut IndexMap, - breakages: &'a mut IndexMap, + overrides: &'a mut BTreeMap, + breakages: &'a mut BTreeMap, ) -> BoxFuture<'a, Result<(), Error>> { async move { crate::db::DatabaseModel::new() @@ -332,7 +333,7 @@ pub fn configure<'a, Db: DbHandle>( // create backreferences to pointers let mut sys = pkg_model.clone().system_pointers().get_mut(db).await?; sys.truncate(0); - let mut current_dependencies: IndexMap = dependencies + let mut current_dependencies: BTreeMap = dependencies .0 .iter() .filter_map(|(id, info)| { @@ -353,7 +354,7 @@ pub fn configure<'a, Db: DbHandle>( package_id, CurrentDependencyInfo { pointers: vec![target], - health_checks: IndexSet::new(), + health_checks: BTreeSet::new(), }, ); } diff --git a/appmgr/src/config/spec.rs b/appmgr/src/config/spec.rs index 0c5cd2317..f93257912 100644 --- a/appmgr/src/config/spec.rs +++ b/appmgr/src/config/spec.rs @@ -1,4 +1,5 @@ use std::borrow::{Borrow, Cow}; +use std::collections::BTreeMap; use std::fmt; use std::fmt::Debug; use std::ops::RangeBounds; @@ -6,7 +7,7 @@ use std::sync::Arc; use std::time::Duration; use async_trait::async_trait; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexSet; use itertools::Itertools; use jsonpath_lib::Compiled as CompiledJsonPath; use patch_db::{DbHandle, OptionModel}; @@ -37,7 +38,7 @@ pub trait ValueSpec { &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError>; // returns all pointers that are live in the provided config @@ -152,7 +153,7 @@ where &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { self.inner.update(ctx, db, config_overrides, value).await @@ -193,7 +194,7 @@ where &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { self.inner.update(ctx, db, config_overrides, value).await @@ -267,7 +268,7 @@ where &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { self.inner.update(ctx, db, config_overrides, value).await @@ -378,7 +379,7 @@ impl ValueSpec for ValueSpecAny { &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { match self { @@ -471,7 +472,7 @@ impl ValueSpec for ValueSpecBoolean { &self, ctx: &RpcContext, _db: &mut Db, - _config_overrides: &IndexMap, + _config_overrides: &BTreeMap, _value: &mut Value, ) -> Result<(), ConfigurationError> { Ok(()) @@ -507,7 +508,7 @@ impl DefaultableWith for ValueSpecBoolean { #[serde(rename_all = "kebab-case")] pub struct ValueSpecEnum { pub values: IndexSet, - pub value_names: IndexMap, + pub value_names: BTreeMap, } impl<'de> serde::de::Deserialize<'de> for ValueSpecEnum { fn deserialize>(deserializer: D) -> Result { @@ -516,7 +517,7 @@ impl<'de> serde::de::Deserialize<'de> for ValueSpecEnum { pub struct _ValueSpecEnum { pub values: IndexSet, #[serde(default)] - pub value_names: IndexMap, + pub value_names: BTreeMap, } let mut r#enum = _ValueSpecEnum::deserialize(deserializer)?; @@ -559,7 +560,7 @@ impl ValueSpec for ValueSpecEnum { &self, ctx: &RpcContext, _db: &mut Db, - _config_overrides: &IndexMap, + _config_overrides: &BTreeMap, _value: &mut Value, ) -> Result<(), ConfigurationError> { Ok(()) @@ -644,7 +645,7 @@ where &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { if let Value::Array(ref mut ls) = value { @@ -746,7 +747,7 @@ impl ValueSpec for ValueSpecList { &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { match self { @@ -869,7 +870,7 @@ impl ValueSpec for ValueSpecNumber { &self, ctx: &RpcContext, _db: &mut Db, - _config_overrides: &IndexMap, + _config_overrides: &BTreeMap, _value: &mut Value, ) -> Result<(), ConfigurationError> { Ok(()) @@ -887,56 +888,6 @@ impl ValueSpec for ValueSpecNumber { } } } -// TODO: remove -// #[derive(Clone, Copy, Debug, Serialize)] -// pub struct Number(pub f64); -// impl<'de> serde::de::Deserialize<'de> for Number { -// fn deserialize(deserializer: D) -> Result -// where -// D: serde::de::Deserializer<'de>, -// { -// use serde::de::*; -// struct NumberVisitor; -// impl<'de> Visitor<'de> for NumberVisitor { -// type Value = Number; - -// fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { -// formatter.write_str("a number") -// } -// fn visit_i8(self, value: i8) -> Result { -// Ok(Number(value.into())) -// } -// fn visit_i16(self, value: i16) -> Result { -// Ok(Number(value.into())) -// } -// fn visit_i32(self, value: i32) -> Result { -// Ok(Number(value.into())) -// } -// fn visit_i64(self, value: i64) -> Result { -// Ok(Number(value as f64)) -// } -// fn visit_u8(self, value: u8) -> Result { -// Ok(Number(value.into())) -// } -// fn visit_u16(self, value: u16) -> Result { -// Ok(Number(value.into())) -// } -// fn visit_u32(self, value: u32) -> Result { -// Ok(Number(value.into())) -// } -// fn visit_u64(self, value: u64) -> Result { -// Ok(Number(value as f64)) -// } -// fn visit_f32(self, value: f32) -> Result { -// Ok(Number(value.into())) -// } -// fn visit_f64(self, value: f64) -> Result { -// Ok(Number(value)) -// } -// } -// deserializer.deserialize_any(NumberVisitor) -// } -// } impl DefaultableWith for ValueSpecNumber { type DefaultSpec = Option; type Error = crate::util::Never; @@ -981,7 +932,7 @@ impl ValueSpec for ValueSpecObject { &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { if let Value::Object(o) = value { @@ -1042,7 +993,7 @@ impl Defaultable for ValueSpecObject { } #[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct ConfigSpec(pub IndexMap); +pub struct ConfigSpec(pub BTreeMap); impl ConfigSpec { pub fn matches(&self, value: &Config) -> Result<(), NoMatchWithPath> { for (key, val) in self.0.iter() { @@ -1080,7 +1031,7 @@ impl ConfigSpec { &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, cfg: &mut Config, ) -> Result<(), ConfigurationError> { for (k, vs) in self.0.iter() { @@ -1171,7 +1122,7 @@ impl ValueSpec for ValueSpecString { &self, ctx: &RpcContext, _db: &mut Db, - _config_overrides: &IndexMap, + _config_overrides: &BTreeMap, _value: &mut Value, ) -> Result<(), ConfigurationError> { Ok(()) @@ -1263,14 +1214,14 @@ pub struct UnionTag { pub id: String, pub name: String, pub description: Option, - pub variant_names: IndexMap, + pub variant_names: BTreeMap, } #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "kebab-case")] pub struct ValueSpecUnion { pub tag: UnionTag, - pub variants: IndexMap, + pub variants: BTreeMap, pub display_as: Option, pub unique_by: UniqueBy, } @@ -1287,7 +1238,7 @@ impl<'de> serde::de::Deserialize<'de> for ValueSpecUnion { #[derive(Deserialize)] #[serde(rename_all = "kebab-case")] pub struct _ValueSpecUnion { - pub variants: IndexMap, + pub variants: BTreeMap, pub tag: _UnionTag, pub display_as: Option, #[serde(default)] @@ -1381,7 +1332,7 @@ impl ValueSpec for ValueSpecUnion { &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { if let Value::Object(o) = value { @@ -1522,7 +1473,7 @@ impl ValueSpec for ValueSpecPointer { &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { match self { @@ -1561,7 +1512,7 @@ impl PackagePointerSpec { &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, ) -> Result { match &self.target { PackagePointerSpecVariant::TorAddress { interface } => { @@ -1665,7 +1616,7 @@ impl ValueSpec for PackagePointerSpec { &self, ctx: &RpcContext, db: &mut Db, - config_overrides: &IndexMap, + config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { *value = self.deref(ctx, db, config_overrides).await?; @@ -1782,7 +1733,7 @@ impl ValueSpec for SystemPointerSpec { &self, ctx: &RpcContext, db: &mut Db, - _config_overrides: &IndexMap, + _config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { *value = self.deref(db).await?; diff --git a/appmgr/src/control.rs b/appmgr/src/control.rs index f8740bfb0..b67deeac2 100644 --- a/appmgr/src/control.rs +++ b/appmgr/src/control.rs @@ -1,6 +1,7 @@ +use std::collections::BTreeMap; + use anyhow::anyhow; use chrono::Utc; -use indexmap::IndexMap; use patch_db::DbHandle; use rpc_toolkit::command; @@ -41,7 +42,7 @@ pub async fn start( *status = MainStatus::Running { started: Utc::now(), - health: IndexMap::new(), + health: BTreeMap::new(), }; status .synchronize( diff --git a/appmgr/src/db/model.rs b/appmgr/src/db/model.rs index 03f522f4a..087e081e0 100644 --- a/appmgr/src/db/model.rs +++ b/appmgr/src/db/model.rs @@ -1,6 +1,6 @@ +use std::collections::{BTreeMap, BTreeSet}; use std::sync::Arc; -use indexmap::{IndexMap, IndexSet}; use patch_db::json_ptr::JsonPointer; use patch_db::{HasModel, Map, MapModel, OptionModel}; use reqwest::Url; @@ -130,7 +130,7 @@ pub struct ConnectionAddresses { } #[derive(Debug, Default, Deserialize, Serialize)] -pub struct AllPackageData(pub IndexMap); +pub struct AllPackageData(pub BTreeMap); impl Map for AllPackageData { type Key = PackageId; type Value = PackageDataEntry; @@ -216,11 +216,11 @@ pub struct InstalledPackageDataEntry { pub manifest: Manifest, pub system_pointers: Vec, #[model] - pub dependency_info: IndexMap, + pub dependency_info: BTreeMap, #[model] - pub current_dependents: IndexMap, + pub current_dependents: BTreeMap, #[model] - pub current_dependencies: IndexMap, + pub current_dependencies: BTreeMap, #[model] pub interface_addresses: InterfaceAddressMap, } @@ -236,11 +236,11 @@ pub struct StaticDependencyInfo { #[serde(rename_all = "kebab-case")] pub struct CurrentDependencyInfo { pub pointers: Vec, - pub health_checks: IndexSet, + pub health_checks: BTreeSet, } #[derive(Debug, Deserialize, Serialize)] -pub struct InterfaceAddressMap(pub IndexMap); +pub struct InterfaceAddressMap(pub BTreeMap); impl Map for InterfaceAddressMap { type Key = InterfaceId; type Value = InterfaceAddresses; diff --git a/appmgr/src/dependencies.rs b/appmgr/src/dependencies.rs index d762b5f81..87574027d 100644 --- a/appmgr/src/dependencies.rs +++ b/appmgr/src/dependencies.rs @@ -1,6 +1,7 @@ +use std::collections::BTreeMap; + use anyhow::anyhow; use emver::VersionRange; -use indexmap::IndexMap; use patch_db::{DbHandle, DiffPatch, HasModel, Map, MapModel}; use serde::{Deserialize, Serialize}; @@ -29,7 +30,7 @@ pub enum DependencyError { }, // { "type": "config-unsatisfied", "error": "Bitcoin Core must have pruning set to manual." } NotRunning, // { "type": "not-running" } HealthChecksFailed { - failures: IndexMap, + failures: BTreeMap, }, // { "type": "health-checks-failed", "checks": { "rpc": { "time": "2021-05-11T18:21:29Z", "result": "warming-up" } } } } impl DependencyError { @@ -100,11 +101,11 @@ pub struct TaggedDependencyError { #[serde(rename_all = "kebab-case")] pub struct BreakageRes { pub patch: DiffPatch, - pub breakages: IndexMap, + pub breakages: BTreeMap, } #[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct Dependencies(pub IndexMap); +pub struct Dependencies(pub BTreeMap); impl Map for Dependencies { type Key = PackageId; type Value = DepInfo; @@ -209,7 +210,7 @@ impl DepInfo { health, } | MainStatus::Running { health, .. } => { - let mut failures = IndexMap::with_capacity(health.len()); + let mut failures = BTreeMap::new(); for (check, res) in health { if !matches!(res.result, HealthCheckResultVariant::Success) { failures.insert(check.clone(), res.clone()); diff --git a/appmgr/src/disk/util.rs b/appmgr/src/disk/util.rs index 930f94588..df27583db 100644 --- a/appmgr/src/disk/util.rs +++ b/appmgr/src/disk/util.rs @@ -1,8 +1,9 @@ +use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use anyhow::anyhow; use futures::TryStreamExt; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexSet; use regex::Regex; use serde::{Deserialize, Serialize}; use tokio::fs::File; @@ -141,7 +142,7 @@ pub async fn list() -> Result, Error> { crate::ErrorKind::Filesystem, ) }) - .try_fold(IndexMap::new(), |mut disks, dir_entry| async move { + .try_fold(BTreeMap::new(), |mut disks, dir_entry| async move { if let Some(disk_path) = dir_entry.path().file_name().and_then(|s| s.to_str()) { let (disk_path, part_path) = if let Some(end) = PARTITION_REGEX.find(disk_path) { ( @@ -169,7 +170,7 @@ pub async fn list() -> Result, Error> { part_path.display().to_string(), ) })?; - disks[&disk].insert(part); + disks.get_mut(&disk).unwrap().insert(part); } } Ok(disks) diff --git a/appmgr/src/id.rs b/appmgr/src/id.rs index 351226dfb..f987569c4 100644 --- a/appmgr/src/id.rs +++ b/appmgr/src/id.rs @@ -69,7 +69,7 @@ impl<'de> Deserialize<'de> for IdUnchecked<&'de str> { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Id = String>(S); impl> Id { pub fn try_from(value: S) -> Result { @@ -137,7 +137,7 @@ impl> Serialize for Id { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct ImageId = String>(Id); impl> std::fmt::Display for ImageId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/appmgr/src/install/mod.rs b/appmgr/src/install/mod.rs index 935eab1ce..8df85d61a 100644 --- a/appmgr/src/install/mod.rs +++ b/appmgr/src/install/mod.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::{BTreeMap, HashSet}; use std::io::SeekFrom; use std::path::Path; use std::process::Stdio; @@ -9,7 +9,6 @@ use anyhow::anyhow; use emver::VersionRange; use futures::TryStreamExt; use http::StatusCode; -use indexmap::IndexMap; use patch_db::DbHandle; use reqwest::Response; use rpc_toolkit::command; @@ -287,7 +286,7 @@ pub async fn install_s9pk( log::info!("Install {}@{}: Unpacked Manifest", pkg_id, version); log::info!("Install {}@{}: Fetching Dependency Info", pkg_id, version); - let mut dependency_info = IndexMap::with_capacity(manifest.dependencies.0.len()); + let mut dependency_info = BTreeMap::new(); let reg_url = ctx.package_registry_url().await?; for (dep, info) in &manifest.dependencies.0 { let manifest: Option = match reqwest::get(format!( @@ -542,7 +541,7 @@ pub async fn install_s9pk( update_current_dependents(&mut tx, pkg_id, ¤t_dependencies).await?; let current_dependents = { // search required dependencies - let mut deps = IndexMap::new(); + let mut deps = BTreeMap::new(); for package in crate::db::DatabaseModel::new() .package_data() .keys(&mut tx, true) @@ -648,8 +647,8 @@ pub async fn install_s9pk( None, &None, false, - &mut IndexMap::new(), - &mut IndexMap::new(), + &mut BTreeMap::new(), + &mut BTreeMap::new(), ) .await?; todo!("set as running if viable"); diff --git a/appmgr/src/net/interface.rs b/appmgr/src/net/interface.rs index 970cb11cc..76d14fb8c 100644 --- a/appmgr/src/net/interface.rs +++ b/appmgr/src/net/interface.rs @@ -1,9 +1,9 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::path::Path; use anyhow::anyhow; use futures::TryStreamExt; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexSet; use itertools::Either; use serde::{Deserialize, Deserializer, Serialize}; use sqlx::{Executor, Sqlite}; @@ -17,7 +17,7 @@ use crate::Error; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] -pub struct Interfaces(pub IndexMap); // TODO +pub struct Interfaces(pub BTreeMap); // TODO impl Interfaces { pub async fn install( &self, @@ -27,7 +27,7 @@ impl Interfaces { where for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, { - let mut interface_addresses = InterfaceAddressMap(IndexMap::new()); + let mut interface_addresses = InterfaceAddressMap(BTreeMap::new()); for (id, iface) in &self.0 { let mut addrs = InterfaceAddresses { tor_address: None, @@ -101,7 +101,7 @@ impl Interfaces { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct InterfaceId = String>(Id); impl> From> for InterfaceId { fn from(id: Id) -> Self { @@ -148,7 +148,7 @@ pub struct Interface { pub name: String, pub description: String, pub tor_config: Option, - pub lan_config: Option>, + pub lan_config: Option>, pub ui: bool, pub protocols: IndexSet, } @@ -156,7 +156,7 @@ pub struct Interface { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct TorConfig { - pub port_mapping: IndexMap, + pub port_mapping: BTreeMap, } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/appmgr/src/net/nginx.rs b/appmgr/src/net/nginx.rs index d4c6f8517..751c5e61d 100644 --- a/appmgr/src/net/nginx.rs +++ b/appmgr/src/net/nginx.rs @@ -1,9 +1,9 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::net::Ipv4Addr; use std::path::PathBuf; use futures::FutureExt; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexSet; use sqlx::SqlitePool; use tokio::sync::Mutex; @@ -201,6 +201,6 @@ struct PackageNetInfo { } pub struct InterfaceMetadata { pub dns_base: String, - pub lan_config: IndexMap, + pub lan_config: BTreeMap, pub protocols: IndexSet, } diff --git a/appmgr/src/s9pk/manifest.rs b/appmgr/src/s9pk/manifest.rs index 66e51a97e..a5df1618c 100644 --- a/appmgr/src/s9pk/manifest.rs +++ b/appmgr/src/s9pk/manifest.rs @@ -19,7 +19,7 @@ use crate::volume::Volumes; pub const SYSTEM_PACKAGE_ID: PackageId<&'static str> = PackageId(SYSTEM_ID); -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PackageId = String>(Id); impl<'a> PackageId<&'a str> { pub fn owned(&self) -> PackageId { diff --git a/appmgr/src/status/health_check.rs b/appmgr/src/status/health_check.rs index a267151e9..b70782854 100644 --- a/appmgr/src/status/health_check.rs +++ b/appmgr/src/status/health_check.rs @@ -1,7 +1,7 @@ +use std::collections::BTreeMap; use std::path::Path; use chrono::{DateTime, Utc}; -use indexmap::IndexMap; use serde::{Deserialize, Deserializer, Serialize}; use crate::action::{ActionImplementation, NoOutput}; @@ -12,7 +12,7 @@ use crate::util::Version; use crate::volume::Volumes; use crate::Error; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct HealthCheckId = String>(Id); impl> std::fmt::Display for HealthCheckId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -43,7 +43,7 @@ impl> AsRef for HealthCheckId { } #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct HealthChecks(pub IndexMap); +pub struct HealthChecks(pub BTreeMap); impl HealthChecks { pub async fn check_all( &self, @@ -52,7 +52,7 @@ impl HealthChecks { pkg_id: &PackageId, pkg_version: &Version, volumes: &Volumes, - ) -> Result, Error> { + ) -> Result, Error> { let res = futures::future::try_join_all(self.0.iter().map(|(id, check)| async move { Ok::<_, Error>(( id.clone(), diff --git a/appmgr/src/status/mod.rs b/appmgr/src/status/mod.rs index 0c3e45e60..f525c17ac 100644 --- a/appmgr/src/status/mod.rs +++ b/appmgr/src/status/mod.rs @@ -1,11 +1,10 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::sync::Arc; use anyhow::anyhow; use chrono::{DateTime, Utc}; use futures::future::BoxFuture; use futures::{FutureExt, StreamExt}; -use indexmap::IndexMap; use patch_db::{DbHandle, HasModel, Map, MapModel, ModelData}; use serde::{Deserialize, Serialize}; @@ -129,7 +128,7 @@ pub async fn check_all(ctx: &RpcContext) -> Result<(), Error> { .to_owned() .into_iter() .filter(|(id, _)| listed_deps.contains(id)) - .collect::>() + .collect::>() }), )); } @@ -182,7 +181,7 @@ pub async fn check_all(ctx: &RpcContext) -> Result<(), Error> { id: &PackageId, statuses: Arc>, model: InstalledPackageDataEntryModel, - current_deps: Arc>, + current_deps: Arc>, mut db: Db, ) -> Result<(), Error> { for (dep_id, dep_info) in &*current_deps { @@ -192,7 +191,7 @@ pub async fn check_all(ctx: &RpcContext) -> Result<(), Error> { started: Some(_), ref health, }) => { - let mut failures = IndexMap::new(); + let mut failures = BTreeMap::new(); for check in &dep_info.health_checks { let res = health .get(check) @@ -219,7 +218,7 @@ pub async fn check_all(ctx: &RpcContext) -> Result<(), Error> { dep_id, model.clone(), err, - &mut IndexMap::new(), + &mut BTreeMap::new(), ) .await?; } else { @@ -274,11 +273,11 @@ pub enum MainStatus { Stopping, Running { started: DateTime, - health: IndexMap, + health: BTreeMap, }, BackingUp { started: Option>, - health: IndexMap, + health: BTreeMap, }, Restoring { running: bool, @@ -394,7 +393,7 @@ impl MainStatus { } #[derive(Debug, Clone, Default, Deserialize, Serialize)] -pub struct DependencyErrors(pub IndexMap); +pub struct DependencyErrors(pub BTreeMap); impl Map for DependencyErrors { type Key = PackageId; type Value = DependencyError; @@ -410,9 +409,9 @@ impl DependencyErrors { ctx: &RpcContext, db: &mut Db, manifest: &Manifest, - current_dependencies: &IndexMap, + current_dependencies: &BTreeMap, ) -> Result { - let mut res = IndexMap::new(); + let mut res = BTreeMap::new(); for dep_id in current_dependencies.keys() { if let Err(e) = manifest .dependencies @@ -448,7 +447,7 @@ pub fn handle_broken_dependents<'a, Db: DbHandle>( dependency: &'a PackageId, model: InstalledPackageDataEntryModel, error: DependencyError, - breakages: &'a mut IndexMap, + breakages: &'a mut BTreeMap, ) -> BoxFuture<'a, Result<(), Error>> { async move { let mut status = model.clone().status().get_mut(db).await?; diff --git a/appmgr/src/util/mod.rs b/appmgr/src/util/mod.rs index adab08a65..fe59f6aa6 100644 --- a/appmgr/src/util/mod.rs +++ b/appmgr/src/util/mod.rs @@ -704,7 +704,68 @@ impl std::io::Write for HashWriter { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub fn deserialize_number_permissive< + 'de, + D: serde::de::Deserializer<'de>, + T: FromStr + num::cast::FromPrimitive, + E: std::fmt::Display, +>( + deserializer: D, +) -> std::result::Result { + use num::cast::FromPrimitive; + + struct Visitor + num::cast::FromPrimitive, E>(std::marker::PhantomData); + impl<'de, T: FromStr + num::cast::FromPrimitive, Err: std::fmt::Display> + serde::de::Visitor<'de> for Visitor + { + type Value = T; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "a parsable string") + } + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse().map_err(|e| serde::de::Error::custom(e)) + } + fn visit_f64(self, v: f64) -> Result + where + E: serde::de::Error, + { + T::from_f64(v).ok_or_else(|| { + serde::de::Error::custom(format!( + "{} cannot be represented by the requested type", + v + )) + }) + } + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, + { + T::from_u64(v).ok_or_else(|| { + serde::de::Error::custom(format!( + "{} cannot be represented by the requested type", + v + )) + }) + } + fn visit_i64(self, v: i64) -> Result + where + E: serde::de::Error, + { + T::from_i64(v).ok_or_else(|| { + serde::de::Error::custom(format!( + "{} cannot be represented by the requested type", + v + )) + }) + } + } + deserializer.deserialize_str(Visitor(std::marker::PhantomData)) +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Port(pub u16); impl<'de> Deserialize<'de> for Port { fn deserialize(deserializer: D) -> Result @@ -712,7 +773,7 @@ impl<'de> Deserialize<'de> for Port { D: Deserializer<'de>, { //TODO: if number, be permissive - deserialize_from_str(deserializer).map(Port) + deserialize_number_permissive(deserializer).map(Port) } } impl Serialize for Port { diff --git a/appmgr/src/volume.rs b/appmgr/src/volume.rs index 5e38ed1fb..b3e08070f 100644 --- a/appmgr/src/volume.rs +++ b/appmgr/src/volume.rs @@ -1,8 +1,8 @@ use std::borrow::Borrow; +use std::collections::BTreeMap; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; -use indexmap::IndexMap; use patch_db::{HasModel, Map, MapModel}; use serde::{Deserialize, Deserializer, Serialize}; @@ -16,7 +16,7 @@ use crate::Error; pub const PKG_VOLUME_DIR: &'static str = "package-data/volumes"; pub const BACKUP_DIR: &'static str = "/mnt/embassy-os-backups/EmbassyBackups"; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum VolumeId = String> { Backup, Custom(Id), @@ -73,7 +73,7 @@ impl> Serialize for VolumeId { } #[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct Volumes(IndexMap); +pub struct Volumes(BTreeMap); impl Volumes { pub async fn install( &self, @@ -111,7 +111,7 @@ impl Volumes { } } impl Deref for Volumes { - type Target = IndexMap; + type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.0 } diff --git a/patch-db b/patch-db index d26e1563e..6f3921e32 160000 --- a/patch-db +++ b/patch-db @@ -1 +1 @@ -Subproject commit d26e1563e87a7a59d328176b810429ab4056cb17 +Subproject commit 6f3921e329445c3a356cbcdf65daefa37c15e45b