From 8edf08299bfe3befacd94696d47780089d6b5511 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 11 Oct 2022 12:01:48 -0400 Subject: [PATCH] FileDB works --- controller/src/database.rs | 2 +- controller/src/filedatabase.rs | 119 +++++++++++--------------------- controller/src/main.rs | 2 +- controller/src/model/member.rs | 19 ++++- controller/src/model/network.rs | 7 ++ 5 files changed, 64 insertions(+), 85 deletions(-) diff --git a/controller/src/database.rs b/controller/src/database.rs index 606d299cf..d4fd2ee6f 100644 --- a/controller/src/database.rs +++ b/controller/src/database.rs @@ -10,7 +10,7 @@ use crate::model::*; #[async_trait] pub trait Database: Sync + Send + NodeStorage + 'static { async fn get_network(&self, id: NetworkId) -> Result, Box>; - async fn save_network(&self, obj: &Network) -> Result<(), Box>; + async fn save_network(&self, obj: Network) -> Result<(), Box>; async fn list_members(&self, network_id: NetworkId) -> Result, Box>; async fn get_member(&self, network_id: NetworkId, node_id: Address) -> Result, Box>; diff --git a/controller/src/filedatabase.rs b/controller/src/filedatabase.rs index b289a361e..1be31751d 100644 --- a/controller/src/filedatabase.rs +++ b/controller/src/filedatabase.rs @@ -4,16 +4,12 @@ use std::str::FromStr; use async_trait::async_trait; -use serde::de::DeserializeOwned; -use serde::Serialize; - use zerotier_network_hypervisor::vl1::{Address, Identity, NodeStorage}; use zerotier_network_hypervisor::vl2::NetworkId; use zerotier_utils::io::{fs_restrict_permissions, read_limit}; -use zerotier_utils::json::{json_patch, to_json_pretty}; +use zerotier_utils::json::to_json_pretty; use zerotier_utils::tokio::fs; -use zerotier_utils::tokio::io::ErrorKind; use crate::database::Database; use crate::model::*; @@ -27,7 +23,6 @@ const IDENTITY_SECRET_FILENAME: &'static str = "identity.secret"; /// the cache. The cache will also contain any ephemeral data, generated data, etc. pub struct FileDatabase { base_path: PathBuf, - cache_path: PathBuf, } fn network_path(base: &PathBuf, network_id: NetworkId) -> PathBuf { @@ -39,32 +34,10 @@ fn member_path(base: &PathBuf, network_id: NetworkId, member_id: Address) -> Pat } impl FileDatabase { - pub async fn new>(base_path: P) -> Self { + pub async fn new>(base_path: P) -> Result> { let base: PathBuf = base_path.as_ref().into(); - let cache: PathBuf = base_path.as_ref().join("cache"); - let _ = fs::create_dir_all(&cache).await; - Self { base_path: base, cache_path: cache } - } - - /// 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; - } - - return Ok(patched); + let _ = fs::create_dir_all(&base).await?; + Ok(Self { base_path: base }) } } @@ -95,34 +68,16 @@ impl Database for FileDatabase { async fn get_network(&self, id: NetworkId) -> Result, Box> { let r = fs::read(network_path(&self.base_path, id)).await; 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_cache(network_path(&self.cache_path, id), network).await?)); - } else { - return Err(Box::new(r.err().unwrap())); - } + Ok(Some(serde_json::from_slice::(raw.as_slice())?)) } else { - let e = r.unwrap_err(); - if matches!(e.kind(), ErrorKind::NotFound) { - let _ = fs::remove_dir_all(self.cache_path.join(id.to_string())).await; - return Ok(None); - } else { - return Err(Box::new(e)); - } + Ok(None) } } - async fn save_network(&self, obj: &Network) -> Result<(), Box> { - let _ = fs::create_dir_all(self.base_path.join(obj.id.to_string())).await; - let _ = fs::create_dir_all(self.cache_path.join(obj.id.to_string())).await; - + async fn save_network(&self, obj: Network) -> Result<(), Box> { let base_network_path = network_path(&self.base_path, 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.cache_path, obj.id), serde_json::to_vec(obj)?.as_slice()).await?; - + let _ = fs::create_dir_all(base_network_path.parent().unwrap()).await; + let _ = fs::write(base_network_path, to_json_pretty(&obj).as_bytes()).await?; Ok(()) } @@ -149,37 +104,16 @@ impl Database for FileDatabase { async fn get_member(&self, network_id: NetworkId, node_id: Address) -> Result, Box> { let r = fs::read(member_path(&self.base_path, network_id, node_id)).await; if let Ok(raw) = r { - let r = serde_json::from_slice::(raw.as_slice()); - if let Ok(member) = r { - return Ok(Some( - self.merge_with_cache(member_path(&self.cache_path, network_id, node_id), member) - .await?, - )); - } else { - return Err(Box::new(r.err().unwrap())); - } + Ok(Some(serde_json::from_slice::(raw.as_slice())?)) } else { - let e = r.unwrap_err(); - if matches!(e.kind(), ErrorKind::NotFound) { - let _ = fs::remove_file(member_path(&self.cache_path, network_id, node_id)).await; - return Ok(None); - } else { - return Err(Box::new(e)); - } + Ok(None) } } async fn save_member(&self, obj: Member) -> Result<(), Box> { let base_member_path = member_path(&self.base_path, obj.network_id, obj.node_id); - if !fs::metadata(&base_member_path).await.is_ok() { - fs::write(base_member_path, to_json_pretty(&obj).as_bytes()).await?; - } - - fs::write( - member_path(&self.cache_path, obj.network_id, obj.node_id), - serde_json::to_vec(&obj)?.as_slice(), - ) - .await?; + let _ = fs::create_dir_all(base_member_path.parent().unwrap()).await; + let _ = fs::write(base_member_path, to_json_pretty(&obj).as_bytes()).await?; Ok(()) } @@ -195,5 +129,30 @@ mod tests { use super::*; #[test] - fn test_db() {} + fn test_db() { + if let Ok(tokio_runtime) = zerotier_utils::tokio::runtime::Builder::new_current_thread().enable_all().build() { + let _ = tokio_runtime.block_on(async { + let node_id = Address::from_u64(0xdeadbeefu64).unwrap(); + let network_id = NetworkId::from_u64(0xfeedbeefcafebabeu64).unwrap(); + + let test_dir = std::env::temp_dir().join("zt_filedatabase_test"); + println!("test filedatabase is in: {}", test_dir.as_os_str().to_str().unwrap()); + + let _ = std::fs::remove_dir_all(&test_dir); + + let db = FileDatabase::new(test_dir).await.expect("new db"); + let mut test_member = Member::new_without_identity(node_id, network_id); + + for x in 0..3 { + test_member.name = x.to_string(); + db.save_member(test_member.clone()).await.expect("member save ok"); + + let test_member2 = db.get_member(network_id, node_id).await.unwrap().unwrap(); + //println!("{}", test_member.to_string()); + //println!("{}", test_member2.to_string()); + assert!(test_member == test_member2); + } + }); + } + } } diff --git a/controller/src/main.rs b/controller/src/main.rs index 67fe2dfc4..2aae68638 100644 --- a/controller/src/main.rs +++ b/controller/src/main.rs @@ -82,7 +82,7 @@ fn main() { if let Ok(tokio_runtime) = zerotier_utils::tokio::runtime::Builder::new_multi_thread().enable_all().build() { tokio_runtime.block_on(async { if let Some(filedb_base_path) = global_args.value_of("filedb") { - std::process::exit(run(Arc::new(FileDatabase::new(filedb_base_path).await), &tokio_runtime).await); + std::process::exit(run(Arc::new(FileDatabase::new(filedb_base_path).await.unwrap()), &tokio_runtime).await); } else { eprintln!("FATAL: no database type selected."); std::process::exit(exitcode::ERR_USAGE); diff --git a/controller/src/model/member.rs b/controller/src/model/member.rs index 3869b1b4e..b2d54dc7d 100644 --- a/controller/src/model/member.rs +++ b/controller/src/model/member.rs @@ -68,11 +68,11 @@ pub struct Member { } impl Member { - pub fn new_with_identity(identity: Identity, network_id: NetworkId) -> Self { + pub fn new_without_identity(node_id: Address, network_id: NetworkId) -> Self { Self { - node_id: identity.address, + node_id, network_id, - identity: Some(identity), + identity: None, name: String::new(), last_authorized_time: None, last_deauthorized_time: None, @@ -86,6 +86,12 @@ impl Member { } } + pub fn new_with_identity(identity: Identity, network_id: NetworkId) -> Self { + let mut tmp = Self::new_without_identity(identity.address, network_id); + tmp.identity = Some(identity); + tmp + } + /// Check whether this member is authorized, which is true if the last authorized time is after last deauthorized time. pub fn authorized(&self) -> bool { self.last_authorized_time @@ -101,6 +107,13 @@ impl Hash for Member { } } +impl ToString for Member { + #[inline(always)] + fn to_string(&self) -> String { + zerotier_utils::json::to_json_pretty(self) + } +} + #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Tag { pub id: u32, diff --git a/controller/src/model/network.rs b/controller/src/model/network.rs index 96e94a283..f29e3e5f5 100644 --- a/controller/src/model/network.rs +++ b/controller/src/model/network.rs @@ -118,6 +118,13 @@ impl Hash for Network { } } +impl ToString for Network { + #[inline(always)] + fn to_string(&self) -> String { + zerotier_utils::json::to_json_pretty(self) + } +} + #[inline(always)] fn troo() -> bool { true