From 433c6058d24d501710c62af917a56083e9646d6a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Sep 2022 11:24:42 -0400 Subject: [PATCH] cleanup --- controller/Cargo.toml | 1 + controller/src/filedatabase.rs | 71 +++++++++++++++++++--------------- controller/src/model.rs | 11 +++++- service/Cargo.toml | 1 - 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/controller/Cargo.toml b/controller/Cargo.toml index a0093af8e..c824b4107 100644 --- a/controller/Cargo.toml +++ b/controller/Cargo.toml @@ -16,3 +16,4 @@ async-trait = "^0" parking_lot = { version = "^0", features = [], default-features = false } serde = { version = "^1", features = ["derive"], default-features = false } serde_json = { version = "^1", features = ["std"], default-features = false } +clap = { version = "^3", features = ["std", "suggestions"], default-features = false } diff --git a/controller/src/filedatabase.rs b/controller/src/filedatabase.rs index 87ec73eec..cf7d04cfb 100644 --- a/controller/src/filedatabase.rs +++ b/controller/src/filedatabase.rs @@ -18,11 +18,11 @@ use crate::model::*; pub struct FileDatabase { base: PathBuf, - live: PathBuf, + cache: PathBuf, } fn network_path(base: &PathBuf, network_id: NetworkId) -> PathBuf { - base.join(network_id.to_string()).join("config.json") + base.join(network_id.to_string()).join(format!("n{}.json", network_id.to_string())) } fn member_path(base: &PathBuf, network_id: NetworkId, member_id: Address) -> PathBuf { @@ -32,32 +32,30 @@ fn member_path(base: &PathBuf, network_id: NetworkId, member_id: Address) -> Pat impl FileDatabase { pub async fn new>(base_path: P) -> Arc { let base: PathBuf = base_path.as_ref().into(); - let live: PathBuf = base_path.as_ref().join("live"); - let _ = fs::create_dir_all(&live).await; - Arc::new(Self { base, live }) + let cache: PathBuf = base_path.as_ref().join("cache"); + let _ = fs::create_dir_all(&cache).await; + Arc::new(Self { base, cache }) } - async fn merge_with_live(&self, live_path: PathBuf, changes: O) -> O { - if let Ok(changes) = serde_json::to_value(&changes) { - if let Ok(old_raw_json) = fs::read(&live_path).await { - if let Ok(mut patched) = serde_json::from_slice::(old_raw_json.as_slice()) { - json_patch(&mut patched, &changes, 64); - if let Ok(patched) = serde_json::from_value::(patched) { - if let Ok(patched_json) = serde_json::to_vec(&patched) { - if let Ok(to_replace) = fs::read(&live_path).await { - if to_replace.as_slice().eq(patched_json.as_slice()) { - return patched; - } - } - let _ = fs::write(live_path, patched_json.as_slice()).await; - return patched; - } - } - } - } + /// Merge an object with its cached instance and save the result to the 'cache' path. + async fn merge_with_cache( + &self, + object_path_in_cache: PathBuf, + changes: O, + ) -> Result> { + let changes = serde_json::to_value(&changes)?; + let cached_json = fs::read(&object_path_in_cache).await?; + + let mut patched = serde_json::from_slice::(cached_json.as_slice())?; + json_patch(&mut patched, &changes, 64); + let patched = serde_json::from_value::(patched)?; + + let patched_json = serde_json::to_vec(&patched)?; + if !cached_json.as_slice().eq(patched_json.as_slice()) { + let _ = fs::write(object_path_in_cache, patched_json.as_slice()).await; } - // TODO: report error - return changes; + + return Ok(patched); } } @@ -70,14 +68,14 @@ impl Database for FileDatabase { if let Ok(raw) = r { let r = serde_json::from_slice::(raw.as_slice()); if let Ok(network) = r { - return Ok(Some(self.merge_with_live(network_path(&self.live, id), network).await)); + return Ok(Some(self.merge_with_cache(network_path(&self.cache, id), network).await?)); } else { return Err(Box::new(r.err().unwrap())); } } else { let e = r.unwrap_err(); if matches!(e.kind(), ErrorKind::NotFound) { - let _ = fs::remove_dir_all(self.live.join(id.to_string())).await; + let _ = fs::remove_dir_all(self.cache.join(id.to_string())).await; return Ok(None); } else { return Err(Box::new(e)); @@ -87,14 +85,14 @@ impl Database for FileDatabase { async fn save_network(&self, obj: &Network) -> Result<(), Self::Error> { let _ = fs::create_dir_all(self.base.join(obj.id.to_string())).await; - let _ = fs::create_dir_all(self.live.join(obj.id.to_string())).await; + let _ = fs::create_dir_all(self.cache.join(obj.id.to_string())).await; let base_network_path = network_path(&self.base, obj.id); if !fs::metadata(&base_network_path).await.is_ok() { fs::write(base_network_path, to_json_pretty(obj).as_bytes()).await?; } - fs::write(network_path(&self.live, obj.id), serde_json::to_vec(obj)?.as_slice()).await?; + fs::write(network_path(&self.cache, obj.id), serde_json::to_vec(obj)?.as_slice()).await?; Ok(()) } @@ -125,7 +123,7 @@ impl Database for FileDatabase { let r = serde_json::from_slice::(raw.as_slice()); if let Ok(member) = r { return Ok(Some( - self.merge_with_live(member_path(&self.live, network_id, node_id), member).await, + self.merge_with_cache(member_path(&self.cache, network_id, node_id), member).await?, )); } else { return Err(Box::new(r.err().unwrap())); @@ -133,7 +131,7 @@ impl Database for FileDatabase { } else { let e = r.unwrap_err(); if matches!(e.kind(), ErrorKind::NotFound) { - let _ = fs::remove_file(member_path(&self.live, network_id, node_id)).await; + let _ = fs::remove_file(member_path(&self.cache, network_id, node_id)).await; return Ok(None); } else { return Err(Box::new(e)); @@ -148,7 +146,7 @@ impl Database for FileDatabase { } fs::write( - member_path(&self.live, obj.network_id, obj.node_id), + member_path(&self.cache, obj.network_id, obj.node_id), serde_json::to_vec(obj)?.as_slice(), ) .await?; @@ -160,3 +158,12 @@ impl Database for FileDatabase { Ok(()) } } + +#[cfg(test)] +mod tests { + #[allow(unused_imports)] + use super::*; + + #[test] + fn test_db() {} +} diff --git a/controller/src/model.rs b/controller/src/model.rs index fafc8a3e1..8c3402aa3 100644 --- a/controller/src/model.rs +++ b/controller/src/model.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use zerotier_network_hypervisor::vl1::{Address, Endpoint, Identity, InetAddress}; use zerotier_network_hypervisor::vl2::NetworkId; +/// Static string included in JSON-serializable objects to indicate their object type through the API. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ObjectType { #[serde(rename = "network")] @@ -83,7 +84,7 @@ pub struct Network { } impl Hash for Network { - #[inline(always)] + #[inline] fn hash(&self, state: &mut H) { self.id.hash(state) } @@ -138,6 +139,14 @@ pub struct Member { pub objtype: ObjectType, } +impl Hash for Member { + #[inline] + fn hash(&self, state: &mut H) { + self.node_id.hash(state); + self.network_id.hash(state); + } +} + /// A complete network with all member configuration information for import/export or blob storage. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct NetworkExport { diff --git a/service/Cargo.toml b/service/Cargo.toml index 56969e51e..48cac2007 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -18,7 +18,6 @@ serde = { version = "^1", features = ["derive"], default-features = false } serde_json = { version = "^1", features = ["std"], default-features = false } parking_lot = { version = "^0", features = [], default-features = false } clap = { version = "^3", features = ["std", "suggestions"], default-features = false } -log = "^0" [target."cfg(windows)".dependencies] winapi = { version = "^0", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }