From d4d44f7898d7bec7d8fb41073b7a1355cc71c0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Severin=20Alexander=20B=C3=BChler?= <8782386+SeverinAlexB@users.noreply.github.com> Date: Mon, 21 Apr 2025 13:38:01 +0300 Subject: [PATCH] feat: add ability to configure hs config with statictestnet (#104) --- Cargo.lock | 14 +++--- pubky-homeserver/src/core/homeserver_core.rs | 1 + .../src/data_directory/persistent_data_dir.rs | 19 ++++--- pubky-testnet/Cargo.toml | 2 + pubky-testnet/src/ephemeral_testnet.rs | 16 +++--- pubky-testnet/src/main.rs | 19 ++++++- pubky-testnet/src/static_testnet.rs | 50 +++++++++++++------ 7 files changed, 84 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26d502d..98b4512 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -479,9 +479,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.30" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" dependencies = [ "clap_builder", "clap_derive", @@ -489,9 +489,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" dependencies = [ "anstream", "anstyle", @@ -501,9 +501,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -2361,6 +2361,8 @@ name = "pubky-testnet" version = "0.2.0-rc.1" dependencies = [ "anyhow", + "clap", + "dirs", "http-relay", "mainline", "pkarr", diff --git a/pubky-homeserver/src/core/homeserver_core.rs b/pubky-homeserver/src/core/homeserver_core.rs index cfcc7d7..37b8863 100644 --- a/pubky-homeserver/src/core/homeserver_core.rs +++ b/pubky-homeserver/src/core/homeserver_core.rs @@ -103,6 +103,7 @@ impl HomeserverCore { /// - Creates the web server (router) for testing. Use `listen` to start the server. pub async fn new(context: AppContext) -> std::result::Result { let router = Self::create_router(&context); + let (icann_http_handle, icann_http_socket) = Self::start_icann_http_server(&context, router.clone()) .await diff --git a/pubky-homeserver/src/data_directory/persistent_data_dir.rs b/pubky-homeserver/src/data_directory/persistent_data_dir.rs index c2d5a0f..2c69c64 100644 --- a/pubky-homeserver/src/data_directory/persistent_data_dir.rs +++ b/pubky-homeserver/src/data_directory/persistent_data_dir.rs @@ -61,6 +61,18 @@ impl PersistentDataDir { pub fn get_secret_file_path(&self) -> PathBuf { self.expanded_path.join("secret") } + + /// Writes the keypair to the secret file. + /// If the file already exists, it will be overwritten. + pub fn write_keypair(&self, keypair: &pkarr::Keypair) -> anyhow::Result<()> { + let secret_file_path = self.get_secret_file_path(); + let secret = keypair.secret_key(); + let hex_string = hex::encode(secret); + std::fs::write(secret_file_path.clone(), hex_string)?; + std::fs::set_permissions(&secret_file_path, std::fs::Permissions::from_mode(0o600))?; + tracing::info!("Secret file created at {}", secret_file_path.display()); + Ok(()) + } } impl Default for PersistentDataDir { @@ -107,12 +119,7 @@ impl DataDir for PersistentDataDir { let secret_file_path = self.get_secret_file_path(); if !secret_file_path.exists() { // Create a new secret file - let keypair = pkarr::Keypair::random(); - let secret = keypair.secret_key(); - let hex_string = hex::encode(secret); - std::fs::write(secret_file_path.clone(), hex_string)?; - std::fs::set_permissions(&secret_file_path, std::fs::Permissions::from_mode(0o600))?; - tracing::info!("Secret file created at {}", secret_file_path.display()); + self.write_keypair(&pkarr::Keypair::random())?; } // Read the secret file let secret = std::fs::read(secret_file_path)?; diff --git a/pubky-testnet/Cargo.toml b/pubky-testnet/Cargo.toml index 62e0612..d8ef14a 100644 --- a/pubky-testnet/Cargo.toml +++ b/pubky-testnet/Cargo.toml @@ -25,3 +25,5 @@ tempfile = "3.19.1" tracing = "0.1.41" pkarr = { workspace = true } mainline = { workspace = true } +clap = "4.5.36" +dirs = "6.0.0" diff --git a/pubky-testnet/src/ephemeral_testnet.rs b/pubky-testnet/src/ephemeral_testnet.rs index c3a5d3a..a421443 100644 --- a/pubky-testnet/src/ephemeral_testnet.rs +++ b/pubky-testnet/src/ephemeral_testnet.rs @@ -10,35 +10,35 @@ use crate::Testnet; /// - An admin server for the homeserver. pub struct EphemeralTestnet { /// Inner flexible testnet. - pub flexible_testnet: Testnet, + pub testnet: Testnet, } impl EphemeralTestnet { /// Run a new simple testnet. pub async fn start() -> anyhow::Result { let mut me = Self { - flexible_testnet: Testnet::new().await?, + testnet: Testnet::new().await?, }; - me.flexible_testnet.create_http_relay().await?; - me.flexible_testnet.create_homeserver_suite().await?; + me.testnet.create_http_relay().await?; + me.testnet.create_homeserver_suite().await?; Ok(me) } /// Create a new pubky client builder. pub fn pubky_client_builder(&self) -> pubky::ClientBuilder { - self.flexible_testnet.pubky_client_builder() + self.testnet.pubky_client_builder() } /// Create a new pkarr client builder. pub fn pkarr_client_builder(&self) -> pkarr::ClientBuilder { - self.flexible_testnet.pkarr_client_builder() + self.testnet.pkarr_client_builder() } /// Get the homeserver in the testnet. pub fn homeserver_suite(&self) -> &pubky_homeserver::HomeserverSuite { - self.flexible_testnet + self.testnet .homeservers .first() .expect("homeservers should be non-empty") @@ -46,7 +46,7 @@ impl EphemeralTestnet { /// Get the http relay in the testnet. pub fn http_relay(&self) -> &HttpRelay { - self.flexible_testnet + self.testnet .http_relays .first() .expect("http relays should be non-empty") diff --git a/pubky-testnet/src/main.rs b/pubky-testnet/src/main.rs index 9bf755b..3f5a1d0 100644 --- a/pubky-testnet/src/main.rs +++ b/pubky-testnet/src/main.rs @@ -1,8 +1,20 @@ +use std::path::PathBuf; + use anyhow::Result; +use clap::Parser; use pubky_testnet::StaticTestnet; +#[derive(Parser, Debug)] +struct Cli { + /// Optional path to a homeserver config file. This overrides the default config. + #[clap(long)] + homeserver_config: Option, +} + #[tokio::main] async fn main() -> Result<()> { + let args = Cli::parse(); + tracing_subscriber::fmt() .with_env_filter( "pubky_homeserver=debug,http_relay=debug,pkarr_relay=info,tower_http=debug,pubky_testnet=debug" @@ -10,7 +22,12 @@ async fn main() -> Result<()> { ) .init(); - let testnet = StaticTestnet::start().await?; + let testnet = if let Some(config_path) = args.homeserver_config { + StaticTestnet::start_with_homeserver_config(config_path).await? + } else { + StaticTestnet::start().await? + }; + tracing::info!("Testnet running"); tracing::info!( "DHT Bootstrap Nodes: {}", diff --git a/pubky-testnet/src/static_testnet.rs b/pubky-testnet/src/static_testnet.rs index 821df44..9490e4c 100644 --- a/pubky-testnet/src/static_testnet.rs +++ b/pubky-testnet/src/static_testnet.rs @@ -1,11 +1,12 @@ use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, + path::PathBuf, str::FromStr, }; use crate::Testnet; use http_relay::HttpRelay; -use pubky_homeserver::{ConfigToml, DomainPort, HomeserverSuite, MockDataDir, SignupMode}; +use pubky_homeserver::{ConfigToml, DomainPort, HomeserverSuite, MockDataDir}; /// A simple testnet with /// @@ -16,7 +17,9 @@ use pubky_homeserver::{ConfigToml, DomainPort, HomeserverSuite, MockDataDir, Sig /// - An admin server for the homeserver on port 6288. pub struct StaticTestnet { /// Inner flexible testnet. - pub flexible_testnet: Testnet, + pub testnet: Testnet, + /// Optional path to the homeserver config file if set. + pub homeserver_config: Option, #[allow(dead_code)] fixed_bootstrap_node: Option, // Keep alive #[allow(dead_code)] @@ -24,16 +27,27 @@ pub struct StaticTestnet { } impl StaticTestnet { - /// Run a new simple testnet. + /// Run a new static testnet with the default homeserver config. pub async fn start() -> anyhow::Result { + Self::new(None).await + } + + /// Run a new static testnet with a custom homeserver config. + pub async fn start_with_homeserver_config(config_path: PathBuf) -> anyhow::Result { + Self::new(Some(config_path)).await + } + + /// Run a new simple testnet. + pub async fn new(config_path: Option) -> anyhow::Result { let testnet = Testnet::new().await?; let fixed_boostrap = Self::run_fixed_boostrap_node(&testnet.dht.bootstrap) .map_err(|e| anyhow::anyhow!("Failed to run bootstrap node on port 6881: {}", e))?; let mut testnet = Self { - flexible_testnet: testnet, + testnet, fixed_bootstrap_node: fixed_boostrap, temp_dirs: vec![], + homeserver_config: config_path, }; testnet @@ -54,16 +68,16 @@ impl StaticTestnet { /// Create a new pubky client builder. pub fn pubky_client_builder(&self) -> pubky::ClientBuilder { - self.flexible_testnet.pubky_client_builder() + self.testnet.pubky_client_builder() } pub fn pkarr_client_builder(&self) -> pkarr::ClientBuilder { - self.flexible_testnet.pkarr_client_builder() + self.testnet.pkarr_client_builder() } /// Get the homeserver in the testnet. pub fn homeserver_suite(&self) -> &pubky_homeserver::HomeserverSuite { - self.flexible_testnet + self.testnet .homeservers .first() .expect("homeservers should be non-empty") @@ -71,7 +85,7 @@ impl StaticTestnet { /// Get the http relay in the testnet. pub fn http_relay(&self) -> &HttpRelay { - self.flexible_testnet + self.testnet .http_relays .first() .expect("http relays should be non-empty") @@ -79,7 +93,7 @@ impl StaticTestnet { /// Get the pkarr relay in the testnet. pub fn pkarr_relay(&self) -> &pkarr_relay::Relay { - self.flexible_testnet + self.testnet .pkarr_relays .first() .expect("pkarr relays should be non-empty") @@ -92,7 +106,7 @@ impl StaticTestnet { nodes.push(dht.info().local_addr().to_string()); } nodes.extend( - self.flexible_testnet + self.testnet .dht_bootstrap_nodes() .iter() .map(|node| node.to_string()), @@ -130,10 +144,10 @@ impl StaticTestnet { .disable_rate_limiter() .pkarr(|pkarr| { pkarr.no_default_network(); - pkarr.bootstrap(&self.flexible_testnet.dht.bootstrap) + pkarr.bootstrap(&self.testnet.dht.bootstrap) }); let relay = unsafe { builder.run() }.await?; - self.flexible_testnet.pkarr_relays.push(relay); + self.testnet.pkarr_relays.push(relay); self.temp_dirs.push(temp_dir); Ok(()) } @@ -144,20 +158,24 @@ impl StaticTestnet { .http_port(15412) // Random available port .run() .await?; - self.flexible_testnet.http_relays.push(relay); + self.testnet.http_relays.push(relay); Ok(()) } async fn run_fixed_homeserver(&mut self) -> anyhow::Result<()> { + let mut config = if let Some(config_path) = &self.homeserver_config { + ConfigToml::from_file(config_path)? + } else { + ConfigToml::test() + }; let keypair = pkarr::Keypair::from_secret_key(&[0; 32]); - let mut config = ConfigToml::test(); config.pkdns.dht_bootstrap_nodes = Some( self.bootstrap_nodes() .iter() .map(|node| DomainPort::from_str(node).unwrap()) .collect(), ); - config.general.signup_mode = SignupMode::Open; + config.pkdns.dht_relay_nodes = None; config.drive.icann_listen_socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6286); config.drive.pubky_listen_socket = @@ -166,7 +184,7 @@ impl StaticTestnet { let mock = MockDataDir::new(config, Some(keypair))?; let homeserver = HomeserverSuite::start_with_mock_data_dir(mock).await?; - self.flexible_testnet.homeservers.push(homeserver); + self.testnet.homeservers.push(homeserver); Ok(()) } }