Skip to content

Commit

Permalink
support custom config file for private cluster configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
aspect committed Jul 28, 2024
1 parent 422a297 commit d2f19ba
Show file tree
Hide file tree
Showing 13 changed files with 267 additions and 72 deletions.
23 changes: 23 additions & 0 deletions examples/cluster.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#
# Example configuration for private node cluster
# This file should be copied to ~/.kaspa-resolver/cluster.toml
# and edited to match the actual configuration of the cluster.
#
# For testing, run the resolver with the following command:
# cargo run --release -- --trace --verbose --config-file=examples/cluster.toml --auto-update
#

[[node]]
service = "kaspa"
transport-type = "wrpc-borsh"
tls = true
network = "mainnet"
fqdn = "example1.com"

[[node]]
service = "kaspa"
tls = true
transport-type = "wrpc-borsh"
network = "testnet-11"
fqdn = "example2.com"

80 changes: 74 additions & 6 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
pub use clap::Parser;
pub use clap::{ArgAction, Parser};
use std::path::PathBuf;
use std::str::FromStr;

use crate::{log_error, log_success};

#[derive(Debug)]
pub enum Action {
Login,
Expand All @@ -21,6 +24,10 @@ pub struct Args {
pub verbose: bool,
/// Tracing mode
pub trace: bool,
/// Auto-update
pub auto_update: bool,
/// Custom config file
pub user_config: Option<PathBuf>,
// Show node data on each election
// pub election: bool,
// Enable resolver status access via `/status`
Expand All @@ -41,28 +48,43 @@ impl Args {
.arg(arg!(--version "Display software version"))
.arg(arg!(--verbose "Enable verbose logging"))
.arg(arg!(--trace "Enable trace log level"))
// .arg(arg!(--auto-update "Poll configuration updates"))
// .arg(arg!(--election "Show node data on each election"))
// .arg(arg!(--status "Enable `/status` endpoint"))
.arg(
Arg::new("auto-update")
.long("auto-update")
.action(ArgAction::SetTrue)
.help("Poll configuration updates (public nodes only)"),
)
.arg(
Arg::new("rate-limit")
.long("rate-limit")
.value_name("REQUESTS:SECONDS")
.num_args(0..=1)
.num_args(1)
.require_equals(true)
.help("Optional rate limit in the form `<requests>:<seconds>`"),
)
.arg(
Arg::new("config-file")
.long("config-file")
.value_name("config.toml file")
.num_args(1)
.require_equals(true)
.help("TOML config file (absolute or relative to working directory)"),
)
.arg(
Arg::new("listen")
.long("listen")
.value_name("INTERFACE:PORT")
.num_args(0..=1)
.num_args(1)
.require_equals(true)
.help("listen interface and port [default: 127.0.0.1:8989]"),
.help("Listen on custom interface and port [default: 127.0.0.1:8989]"),
)
.subcommand(Command::new("test").about("Test configuration"))
.subcommand(Command::new("login").about("Create local update key"))
.subcommand(Command::new("pack").about("Package configuration"))
.subcommand(Command::new("unpack").about("Package configuration"))
.subcommand(Command::new("pack").about("Pack configuration"))
.subcommand(Command::new("unpack").about("Unpack configuration"))
.subcommand(Command::new("update").about("Update configuration from GitHub"))
// .subcommand(Command::new("reload").about("Reload configuration"))
;
Expand All @@ -71,9 +93,53 @@ impl Args {

let trace = matches.get_one::<bool>("trace").cloned().unwrap_or(false);
let verbose = matches.get_one::<bool>("verbose").cloned().unwrap_or(false);
let auto_update = matches
.get_one::<bool>("auto-update")
.cloned()
.unwrap_or(false);

if auto_update {
log_success!("Update", "Enabling auto-update");
}
// let private_cluster = matches.get_one::<bool>("private-cluster").cloned().unwrap_or(false);
// let election = matches.get_one::<bool>("election").cloned().unwrap_or(false);
// let status = matches.get_one::<bool>("status").cloned().unwrap_or(false);

let user_config = matches.get_one::<String>("config-file").cloned().map(|s| {
if s.contains('~') {
let s = s.replace(
"~",
dirs::home_dir()
.expect("Unable to obtain user home folder")
.to_str()
.unwrap(),
);
PathBuf::from(s)
} else if !s.starts_with('/') {
std::env::current_dir()
.expect("Unable to obtain current working directory")
.join(s)
} else {
PathBuf::from(s)
}
});

if let Some(user_config) = &user_config {
log_success!(
"Config",
"Using custom config file: `{}`",
user_config.display()
);
if auto_update {
log_error!(
"Config",
"Auto-update is not supported with custom local config file..."
);
log_error!("Config", "Halting...");
std::process::exit(1);
}
}

let rate_limit = matches.get_one::<RateLimit>("rate-limit").cloned();
let listen = matches
.get_one::<String>("listen")
Expand All @@ -100,6 +166,8 @@ impl Args {
Args {
trace,
verbose,
auto_update,
user_config,
// election,
// status,
listen,
Expand Down
61 changes: 41 additions & 20 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::sync::LazyLock;

use crate::imports::*;
use chrono::prelude::*;

Expand All @@ -6,25 +8,27 @@ const VERSION: u64 = 2;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
#[serde(rename = "transport")]
transports: TransportDictionary,
transports: Option<TransportDictionary>,
#[serde(rename = "group")]
groups: Option<Vec<Group>>,
#[serde(rename = "node")]
nodes: Option<Vec<NodeConfig>>,
}

impl Config {
pub fn try_parse(toml: &str) -> Result<Vec<Arc<NodeConfig>>> {
pub fn try_parse(toml: &str) -> Result<Vec<Arc<Node>>> {
let config = toml::from_str::<Config>(toml)?;

let mut nodes: Vec<Arc<NodeConfig>> = config
let mut nodes: Vec<Arc<Node>> = config
.nodes
.map(|nodes| {
nodes
.into_iter()
.filter_map(|mut node| {
node.uid = xxh3_64(node.address.as_bytes());
node.enable.unwrap_or(true).then_some(node).map(Arc::new)
.filter_map(|node| {
node.enable
.unwrap_or(true)
.then_some(node.into())
.map(Arc::new)
})
.collect::<Vec<_>>()
})
Expand All @@ -47,7 +51,7 @@ impl Config {
}
}

let transport_dictionary = &config.transports;
let transport_dictionary = &config.transports.unwrap_or_default();

for group in groups.iter() {
if !group.fqdn.contains('*') {
Expand All @@ -69,13 +73,8 @@ impl Config {
let fqdn = fqdn.replace('*', &id.to_lowercase());
let address =
transport.make_address(&fqdn, service, network_id);
let node = NodeConfig::new(
service,
*network_id,
transport,
fqdn,
address,
);
let node =
Node::new(service, *network_id, transport, fqdn, address);
nodes.push(node);
} else {
log_error!("Config", "Unknown transport: {}", transport);
Expand All @@ -91,14 +90,36 @@ impl Config {
}
}

pub fn init() -> Result<()> {
static USER_CONFIG: LazyLock<Mutex<Option<Vec<Arc<Node>>>>> = LazyLock::new(|| Mutex::new(None));

pub fn user_config() -> Option<Vec<Arc<Node>>> {
USER_CONFIG.lock().unwrap().clone()
}

pub fn init(user_config: &Option<PathBuf>) -> Result<()> {
Settings::load();

let global_config_folder = global_config_folder();
if !global_config_folder.exists() {
fs::create_dir_all(&global_config_folder)?;
}

if let Some(user_config) = user_config {
// let config_path = Path::new(config);
if !user_config.exists() {
Err(Error::custom(format!(
"Config file not found: `{}`",
user_config.display()
)))?;
} else {
let toml = fs::read_to_string(user_config)?;
USER_CONFIG
.lock()
.unwrap()
.replace(Config::try_parse(toml.as_str())?);
}
}

Ok(())
}

Expand Down Expand Up @@ -175,21 +196,21 @@ pub fn locate_local_config() -> Option<PathBuf> {
})
}

pub fn test_config() -> Result<Vec<Arc<NodeConfig>>> {
pub fn test_config() -> Result<Vec<Arc<Node>>> {
let local_config = locate_local_config().ok_or(Error::LocalConfigNotFound)?;
let toml = fs::read_to_string(local_config)?;
// let local = include_str!("../Resolver.toml");
Config::try_parse(toml.as_str())
}

pub fn load_config() -> Result<Vec<Arc<NodeConfig>>> {
pub fn load_config() -> Result<Vec<Arc<Node>>> {
match load_global_config() {
Ok(config) => Ok(config),
Err(_) => load_default_config(),
}
}

pub fn load_global_config() -> Result<Vec<Arc<NodeConfig>>> {
pub fn load_global_config() -> Result<Vec<Arc<Node>>> {
let global_config_folder = global_config_folder();
if !global_config_folder.exists() {
fs::create_dir_all(&global_config_folder)?;
Expand All @@ -200,14 +221,14 @@ pub fn load_global_config() -> Result<Vec<Arc<NodeConfig>>> {
Config::try_parse(toml.as_str()?)
}

pub fn load_default_config() -> Result<Vec<Arc<NodeConfig>>> {
pub fn load_default_config() -> Result<Vec<Arc<Node>>> {
let local_config_folder = local_config_folder().ok_or(Error::LocalConfigNotFound)?;
let local_config = local_config_folder.join(local_config_file());
let toml = fs::read_to_string(local_config)?;
Config::try_parse(toml.as_str())
}

pub async fn update_global_config() -> Result<Option<Vec<Arc<NodeConfig>>>> {
pub async fn update_global_config() -> Result<Option<Vec<Arc<Node>>>> {
static HASH: Mutex<Option<Vec<u8>>> = Mutex::new(None);

log_info!("Config", "Updating resolver config");
Expand Down
13 changes: 8 additions & 5 deletions src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub struct Connection {
is_synced: AtomicBool,
clients: AtomicU64,
peers: AtomicU64,
node: Arc<NodeConfig>,
node: Arc<Node>,
monitor: Arc<Monitor>,
params: PathParams,
client: rpc::Client,
Expand All @@ -37,7 +37,7 @@ pub struct Connection {
impl Connection {
pub fn try_new(
monitor: Arc<Monitor>,
node: Arc<NodeConfig>,
node: Arc<Node>,
_sender: Sender<PathParams>,
args: &Arc<Args>,
) -> Result<Self> {
Expand Down Expand Up @@ -145,7 +145,7 @@ impl Connection {
}

#[inline]
pub fn node(&self) -> &Arc<NodeConfig> {
pub fn node(&self) -> &Arc<Node> {
&self.node
}

Expand Down Expand Up @@ -433,6 +433,7 @@ pub struct Status<'a> {
pub encryption: TlsKind,
pub network: &'a NetworkId,
pub cores: u64,
pub memory: u64,
pub status: &'static str,
pub peers: u64,
pub clients: u64,
Expand All @@ -457,7 +458,7 @@ impl<'a> From<&'a Arc<Connection>> for Status<'a> {
let status = connection.status();
let clients = delegate.clients();
let peers = delegate.peers();
let (version, sid, capacity, cores) = delegate
let (version, sid, capacity, cores, memory) = delegate
.caps()
.as_ref()
.as_ref()
Expand All @@ -467,9 +468,10 @@ impl<'a> From<&'a Arc<Connection>> for Status<'a> {
caps.system_id,
caps.clients_limit,
caps.cpu_physical_cores,
caps.total_memory,
)
})
.unwrap_or_else(|| ("n/a".to_string(), 0, 0, 0));
.unwrap_or_else(|| ("n/a".to_string(), 0, 0, 0, 0));

let delegates = connection
.resolve_delegates()
Expand All @@ -490,6 +492,7 @@ impl<'a> From<&'a Arc<Connection>> for Status<'a> {
encryption,
network,
cores,
memory,
status,
clients,
peers,
Expand Down
Loading

0 comments on commit d2f19ba

Please sign in to comment.