FileDB works

This commit is contained in:
Adam Ierymenko 2022-10-11 12:01:48 -04:00
parent dbbb3732b5
commit 8edf08299b
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
5 changed files with 64 additions and 85 deletions

View file

@ -10,7 +10,7 @@ use crate::model::*;
#[async_trait]
pub trait Database: Sync + Send + NodeStorage + 'static {
async fn get_network(&self, id: NetworkId) -> Result<Option<Network>, Box<dyn Error>>;
async fn save_network(&self, obj: &Network) -> Result<(), Box<dyn Error>>;
async fn save_network(&self, obj: Network) -> Result<(), Box<dyn Error>>;
async fn list_members(&self, network_id: NetworkId) -> Result<Vec<Address>, Box<dyn Error>>;
async fn get_member(&self, network_id: NetworkId, node_id: Address) -> Result<Option<Member>, Box<dyn Error>>;

View file

@ -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<P: AsRef<Path>>(base_path: P) -> Self {
pub async fn new<P: AsRef<Path>>(base_path: P) -> Result<Self, Box<dyn Error>> {
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<O: Serialize + DeserializeOwned>(
&self,
object_path_in_cache: PathBuf,
changes: O,
) -> Result<O, Box<dyn Error>> {
let changes = serde_json::to_value(&changes)?;
let cached_json = fs::read(&object_path_in_cache).await?;
let mut patched = serde_json::from_slice::<serde_json::Value>(cached_json.as_slice())?;
json_patch(&mut patched, &changes, 64);
let patched = serde_json::from_value::<O>(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<Option<Network>, Box<dyn Error>> {
let r = fs::read(network_path(&self.base_path, id)).await;
if let Ok(raw) = r {
let r = serde_json::from_slice::<Network>(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::<Network>(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<dyn Error>> {
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<dyn Error>> {
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<Option<Member>, Box<dyn Error>> {
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::<Member>(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::<Member>(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<dyn Error>> {
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);
}
});
}
}
}

View file

@ -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);

View file

@ -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,

View file

@ -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