mod cluster;
use std::fmt::Debug;
use std::path::Path;
mod lease;
mod network;
mod raft;
mod retry;
mod tls;
pub use cluster::*;
use config::ConfigError;
pub use lease::*;
pub use network::*;
pub use raft::*;
pub use retry::*;
pub use tls::*;
#[cfg(test)]
mod config_test;
#[cfg(test)]
mod lease_test;
#[cfg(test)]
mod network_test;
#[cfg(test)]
mod raft_test;
#[cfg(test)]
mod tls_test;
use std::env;
use config::Config;
use config::Environment;
use config::File;
use serde::Deserialize;
use serde::Serialize;
use crate::Error;
use crate::Result;
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct RaftNodeConfig {
pub cluster: ClusterConfig,
pub network: NetworkConfig,
pub raft: RaftConfig,
pub retry: RetryPolicies,
pub tls: TlsConfig,
}
impl Debug for RaftNodeConfig {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
f.debug_struct("RaftNodeConfig").field("cluster", &self.cluster).finish()
}
}
impl RaftNodeConfig {
pub fn new() -> Result<Self> {
let mut builder = Config::builder().add_source(Config::try_from(&Self::default())?);
if let Ok(config_path) = env::var("CONFIG_PATH") {
builder = builder.add_source(File::with_name(&config_path).required(true));
}
builder = builder.add_source(
Environment::with_prefix("RAFT")
.separator("__")
.ignore_empty(true)
.try_parsing(true),
);
let config: Self = builder.build()?.try_deserialize()?;
Ok(config) }
pub fn with_override_config(
&self,
path: &str,
) -> Result<Self> {
let config: Self = Config::builder()
.add_source(Config::try_from(self)?)
.add_source(File::with_name(path))
.add_source(
Environment::with_prefix("RAFT")
.separator("__")
.ignore_empty(true)
.try_parsing(true),
)
.build()?
.try_deserialize()?;
Ok(config) }
pub fn validate(self) -> Result<Self> {
self.cluster.validate()?;
self.raft.validate()?;
self.network.validate()?;
self.tls.validate()?;
self.retry.validate()?;
Ok(self)
}
pub fn is_learner(&self) -> bool {
use d_engine_proto::common::NodeRole;
self.cluster
.initial_cluster
.iter()
.find(|n| n.id == self.cluster.node_id)
.map(|n| n.role == NodeRole::Learner as i32)
.unwrap_or(false)
}
}
pub(super) fn validate_directory(
path: &Path,
name: &str,
) -> Result<()> {
if path.as_os_str().is_empty() {
return Err(Error::Config(ConfigError::Message(format!(
"{name} path cannot be empty"
))));
}
#[cfg(not(test))]
{
use std::fs;
if !path.exists() {
fs::create_dir_all(path).map_err(|e| {
Error::Config(ConfigError::Message(format!(
"Failed to create {} directory at {}: {}",
name,
path.display(),
e
)))
})?;
}
let test_file = path.join(".permission_test");
fs::write(&test_file, b"test").map_err(|e| {
Error::Config(ConfigError::Message(format!(
"No write permission in {} directory {}: {}",
name,
path.display(),
e
)))
})?;
fs::remove_file(&test_file).ok();
}
Ok(())
}