diff --git a/iroh-api/benches/add.rs b/iroh-api/benches/add.rs index 1f94d92be3..170c0d4a66 100644 --- a/iroh-api/benches/add.rs +++ b/iroh-api/benches/add.rs @@ -1,6 +1,5 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use futures::TryStreamExt; -use iroh_metrics::config::Config as MetricsConfig; use iroh_resolver::resolver::Resolver; use iroh_rpc_client::{Client, Config as RpcClientConfig}; use iroh_rpc_types::Addr; @@ -37,15 +36,10 @@ fn add_benchmark(c: &mut Criterion) { let server_addr = Addr::new_mem(); let client_addr = server_addr.clone(); let rpc_client = RpcClientConfig { - store_addr: Some(client_addr), + store_addr: Some(client_addr.clone()), ..Default::default() }; - - let config = StoreConfig { - path: dir.path().join("db"), - rpc_client: rpc_client.clone(), - metrics: MetricsConfig::default(), - }; + let config = StoreConfig::with_rpc_addr(dir.path().join("db"), client_addr); let (_task, client, resolver) = executor.block_on(async { let store = Store::create(config).await.unwrap(); let task = executor.spawn(async move { diff --git a/iroh-embed/src/store.rs b/iroh-embed/src/store.rs index 590e6d6f80..1fc6a321bc 100644 --- a/iroh-embed/src/store.rs +++ b/iroh-embed/src/store.rs @@ -4,7 +4,6 @@ use std::path::PathBuf; use anyhow::Result; use iroh_one::mem_store; -use iroh_rpc_client::Config as RpcClientConfig; use iroh_rpc_types::store::StoreAddr; use iroh_rpc_types::Addr; use iroh_store::Config as StoreConfig; @@ -26,16 +25,7 @@ impl RocksStoreService { /// This implicitly starts a task on the tokio runtime to manage the storage node. pub async fn new(path: PathBuf) -> Result { let addr = Addr::new_mem(); - let config = StoreConfig { - path, - rpc_client: RpcClientConfig { - gateway_addr: None, - p2p_addr: None, - store_addr: Some(addr.clone()), - channels: Some(1), - }, - metrics: Default::default(), - }; + let config = StoreConfig::with_rpc_addr(path, addr.clone()); let task = mem_store::start(addr.clone(), config).await?; Ok(Self { task, addr }) } diff --git a/iroh-gateway/Cargo.toml b/iroh-gateway/Cargo.toml index 8b59368a90..69f3c5a569 100644 --- a/iroh-gateway/Cargo.toml +++ b/iroh-gateway/Cargo.toml @@ -61,6 +61,7 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } tracing.workspace = true url.workspace = true urlencoding.workspace = true +testdir.workspace = true [dev-dependencies] iroh-store.workspace = true diff --git a/iroh-gateway/src/config.rs b/iroh-gateway/src/config.rs index 7d8140c475..f84f593858 100644 --- a/iroh-gateway/src/config.rs +++ b/iroh-gateway/src/config.rs @@ -21,6 +21,43 @@ pub const CONFIG_FILE_NAME: &str = "gateway.config.toml"; pub const ENV_PREFIX: &str = "IROH_GATEWAY"; pub const DEFAULT_PORT: u16 = 9050; +/// The configuration for the gateway server. +/// +/// This is the configuration which the gateway server binary needs to run. This is a +/// superset from the configuration needed by the gateway service, which can also run +/// integrated into another binary like iroh-one, iroh-share or iroh-embed. +// NOTE: If this was moved into main.rs it would not need to be public. Our binary is build +// as being outside of the lib crate, hence here it needs to be public. +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +pub struct ServerConfig { + /// Configuration of the gateway service. + pub gateway: Config, + /// Metrics configuration. + pub metrics: MetricsConfig, +} + +impl ServerConfig { + pub fn new(port: u16, rpc_client: RpcClientConfig) -> Self { + Self { + gateway: Config::new(port, rpc_client), + metrics: MetricsConfig::default(), + } + } +} + +impl Source for ServerConfig { + fn clone_into_box(&self) -> Box { + Box::new(self.clone()) + } + + fn collect(&self) -> Result, ConfigError> { + let mut map: Map = Map::new(); + insert_into_config_map(&mut map, "gateway", self.gateway.collect()?); + insert_into_config_map(&mut map, "metrics", self.metrics.collect()?); + Ok(map) + } +} + /// Configuration for [`iroh-gateway`]. #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct Config { @@ -43,8 +80,6 @@ pub struct Config { pub indexer_endpoint: Option, /// rpc addresses for the gateway & addresses for the rpc client to dial pub rpc_client: RpcClientConfig, - /// metrics configuration - pub metrics: MetricsConfig, // NOTE: for toml to serialize properly, the "table" values must be serialized at the end, and // so much come at the end of the `Config` struct /// set of user provided headers to attach to all responses @@ -65,7 +100,6 @@ impl Config { http_resolvers: None, dns_resolver: DnsResolverConfig::default(), indexer_endpoint: None, - metrics: MetricsConfig::default(), use_denylist: false, redirect_to_subdomain: false, } @@ -80,6 +114,12 @@ impl Config { } } +impl From for Config { + fn from(source: ServerConfig) -> Self { + source.gateway + } +} + fn default_headers() -> HeaderMap { let mut headers = HeaderMap::new(); headers.typed_insert(AccessControlAllowOrigin::ANY); @@ -136,7 +176,6 @@ impl Default for Config { http_resolvers: None, dns_resolver: DnsResolverConfig::default(), indexer_endpoint: None, - metrics: MetricsConfig::default(), use_denylist: false, redirect_to_subdomain: false, }; @@ -160,8 +199,6 @@ impl Source for Config { insert_into_config_map(&mut map, "port", self.port as i32); insert_into_config_map(&mut map, "headers", collect_headers(&self.headers)?); insert_into_config_map(&mut map, "rpc_client", rpc_client); - let metrics = self.metrics.collect()?; - insert_into_config_map(&mut map, "metrics", metrics); if let Some(http_resolvers) = &self.http_resolvers { insert_into_config_map(&mut map, "http_resolvers", http_resolvers.clone()); @@ -209,6 +246,10 @@ fn collect_headers(headers: &HeaderMap) -> Result, ConfigErro #[cfg(test)] mod tests { + use std::collections::HashMap; + + use testdir::testdir; + use super::*; use config::Config as ConfigBuilder; @@ -228,24 +269,11 @@ mod tests { #[test] fn test_collect() { - let default = Config::default(); + let default = ServerConfig::default(); let mut expect: Map = Map::new(); expect.insert( - "public_url_base".to_string(), - Value::new(None, default.public_url_base.clone()), - ); - expect.insert("port".to_string(), Value::new(None, default.port as i64)); - expect.insert( - "use_denylist".to_string(), - Value::new(None, default.use_denylist), - ); - expect.insert( - "headers".to_string(), - Value::new(None, collect_headers(&default.headers).unwrap()), - ); - expect.insert( - "rpc_client".to_string(), - Value::new(None, default.rpc_client.collect().unwrap()), + "gateway".to_string(), + Value::new(None, default.gateway.collect().unwrap()), ); expect.insert( "metrics".to_string(), @@ -301,4 +329,37 @@ mod tests { assert_eq!(expect, got); } + + #[test] + fn test_toml_file() { + let dir = testdir!(); + let cfg_file = dir.join("config.toml"); + std::fs::write( + &cfg_file, + r#" + [gateway] + public_url_base = "http://example.com/base/" + port = 2001 + [gateway.rpc_client] + gateway_addr = "irpc://127.0.0.42:1234" + "#, + ) + .unwrap(); + let sources = [Some(cfg_file.as_path())]; + let cfg = iroh_util::make_config( + ServerConfig::default(), + &sources, + ENV_PREFIX, + HashMap::<&str, String>::new(), + ) + .unwrap(); + dbg!(&cfg); + + assert_eq!(cfg.gateway.public_url_base, "http://example.com/base/"); + assert_eq!(cfg.gateway.port, 2001); + assert_eq!( + cfg.gateway.rpc_client.gateway_addr.unwrap(), + "irpc://127.0.0.42:1234".parse().unwrap(), + ); + } } diff --git a/iroh-gateway/src/core.rs b/iroh-gateway/src/core.rs index ea99a8a69e..32b971721d 100644 --- a/iroh-gateway/src/core.rs +++ b/iroh-gateway/src/core.rs @@ -194,11 +194,7 @@ mod tests { let server_addr = Addr::new_mem(); let client_addr = server_addr.clone(); let store_dir = tempfile::tempdir().unwrap(); - let config = iroh_store::Config { - path: store_dir.path().join("db"), - rpc_client: RpcClientConfig::default(), - metrics: iroh_metrics::config::Config::default(), - }; + let config = iroh_store::Config::new(store_dir.path().join("db")); let store = iroh_store::Store::create(config).await.unwrap(); let task = tokio::spawn(async move { iroh_store::rpc::new(server_addr, store).await.unwrap() }); diff --git a/iroh-gateway/src/main.rs b/iroh-gateway/src/main.rs index e02ee70393..5e6fc7af0f 100644 --- a/iroh-gateway/src/main.rs +++ b/iroh-gateway/src/main.rs @@ -5,7 +5,7 @@ use clap::Parser; use iroh_gateway::{ bad_bits::{self, BadBits}, cli::Args, - config::{Config, CONFIG_FILE_NAME, ENV_PREFIX}, + config::{Config, ServerConfig, CONFIG_FILE_NAME, ENV_PREFIX}, core::Core, metrics, }; @@ -26,7 +26,7 @@ async fn main() -> Result<()> { let sources = [Some(cfg_path.as_path()), args.cfg.as_deref()]; let mut config = make_config( // default - Config::default(), + ServerConfig::default(), // potential config files &sources, // env var prefix for this config @@ -39,11 +39,12 @@ async fn main() -> Result<()> { println!("{config:#?}"); let metrics_config = config.metrics.clone(); - let dns_resolver_config = config.dns_resolver.clone(); - let bad_bits = match config.use_denylist { + let dns_resolver_config = config.gateway.dns_resolver.clone(); + let bad_bits = match config.gateway.use_denylist { true => Arc::new(Some(RwLock::new(BadBits::new()))), false => Arc::new(None), }; + let config = Config::from(config); let rpc_addr = config .rpc_addr() .ok_or_else(|| anyhow!("missing gateway rpc addr"))?; diff --git a/iroh-one/src/config.rs b/iroh-one/src/config.rs index 9e4e86ba11..caf875897f 100644 --- a/iroh-one/src/config.rs +++ b/iroh-one/src/config.rs @@ -76,9 +76,6 @@ impl Config { self.gateway.rpc_client = self.rpc_client.clone(); self.p2p.rpc_client = self.rpc_client.clone(); self.store.rpc_client = self.rpc_client.clone(); - self.gateway.metrics = self.metrics.clone(); - self.p2p.metrics = self.metrics.clone(); - self.store.metrics = self.metrics.clone(); } } @@ -93,15 +90,14 @@ impl Default for Config { .join("ipfsd.http"); let rpc_client = Self::default_rpc_config(); let metrics_config = MetricsConfig::default(); - let store_config = - default_store_config(None, rpc_client.clone(), metrics_config.clone()).unwrap(); + let store_config = default_store_config(None, rpc_client.clone()).unwrap(); let key_store_path = iroh_util::iroh_data_root().unwrap(); Self { rpc_client: rpc_client.clone(), - metrics: metrics_config.clone(), + metrics: metrics_config, gateway: iroh_gateway::config::Config::default(), store: store_config, - p2p: default_p2p_config(rpc_client, metrics_config, key_store_path), + p2p: default_p2p_config(rpc_client, key_store_path), #[cfg(all(feature = "http-uds-gateway", unix))] gateway_uds_path: Some(gateway_uds_path), } @@ -111,26 +107,19 @@ impl Default for Config { fn default_store_config( store_path: Option, ipfsd: RpcClientConfig, - metrics: iroh_metrics::config::Config, ) -> Result { let path = config_data_path(store_path)?; Ok(iroh_store::config::Config { path, rpc_client: ipfsd, - metrics, }) } -fn default_p2p_config( - ipfsd: RpcClientConfig, - metrics: iroh_metrics::config::Config, - key_store_path: PathBuf, -) -> iroh_p2p::config::Config { +fn default_p2p_config(ipfsd: RpcClientConfig, key_store_path: PathBuf) -> iroh_p2p::config::Config { iroh_p2p::config::Config { + key_store_path, libp2p: Libp2pConfig::default(), rpc_client: ipfsd, - metrics, - key_store_path, } } diff --git a/iroh-p2p/src/config.rs b/iroh-p2p/src/config.rs index 2cba51467e..926ad3d133 100644 --- a/iroh-p2p/src/config.rs +++ b/iroh-p2p/src/config.rs @@ -30,6 +30,45 @@ pub const DEFAULT_BOOTSTRAP: &[&str] = &[ // "/ip4/104.131.131.82/udp/4001/quic/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io +/// The configuration for the p2p server. +/// +/// This is the configuration which the p2p server binary needs to run. It is a superset +/// from the configuration needed by the p2p service, which can also run integrated into +/// another binary like in iroh-one, iroh-share or iroh-embed. +#[derive(PartialEq, Debug, Deserialize, Serialize, Clone)] +pub struct ServerConfig { + pub p2p: Config, + pub metrics: MetricsConfig, +} + +impl ServerConfig { + pub fn new() -> Self { + Default::default() + } +} + +impl Default for ServerConfig { + fn default() -> Self { + Self { + p2p: Config::default_network(), + metrics: Default::default(), + } + } +} + +impl Source for ServerConfig { + fn clone_into_box(&self) -> Box { + Box::new(self.clone()) + } + + fn collect(&self) -> Result, ConfigError> { + let mut map: Map = Map::new(); + insert_into_config_map(&mut map, "p2p", self.p2p.collect()?); + insert_into_config_map(&mut map, "metrics", self.metrics.collect()?); + Ok(map) + } +} + /// Libp2p config for the node. #[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)] #[non_exhaustive] @@ -67,9 +106,10 @@ pub struct Libp2pConfig { /// Configuration for the [`iroh-p2p`] node. #[derive(PartialEq, Debug, Clone, Deserialize, Serialize)] pub struct Config { + /// Configuration for libp2p. pub libp2p: Libp2pConfig, + /// Configuration of RPC to other iroh services. pub rpc_client: RpcClientConfig, - pub metrics: MetricsConfig, /// Directory where cryptographic keys are stored. /// /// The p2p node needs to have an identity consisting of a cryptographic key pair. As @@ -79,6 +119,12 @@ pub struct Config { pub key_store_path: PathBuf, } +impl From for Config { + fn from(source: ServerConfig) -> Self { + source.p2p + } +} + impl Source for Libp2pConfig { fn clone_into_box(&self) -> Box { Box::new(self.clone()) @@ -149,10 +195,8 @@ impl Source for Config { fn collect(&self) -> Result, ConfigError> { let mut map: Map = Map::new(); - insert_into_config_map(&mut map, "libp2p", self.libp2p.collect()?); insert_into_config_map(&mut map, "rpc_client", self.rpc_client.collect()?); - insert_into_config_map(&mut map, "metrics", self.metrics.collect()?); insert_into_config_map(&mut map, "key_store_path", self.key_store_path.to_str()); Ok(map) } @@ -199,7 +243,6 @@ impl Config { p2p_addr: Some(client_addr), ..Default::default() }, - metrics: MetricsConfig::default(), key_store_path: iroh_data_root().unwrap(), } } @@ -210,7 +253,6 @@ impl Config { Self { libp2p: Libp2pConfig::default(), rpc_client, - metrics: MetricsConfig::default(), key_store_path: iroh_data_root().unwrap(), } } @@ -227,8 +269,9 @@ mod tests { #[test] fn test_collect() { - let default = Config::default_network(); + let default = ServerConfig::default(); let bootstrap_peers: Vec = default + .p2p .libp2p .bootstrap_peers .iter() @@ -236,6 +279,7 @@ mod tests { .collect(); let addrs: Vec = default + .p2p .libp2p .listening_multiaddrs .iter() @@ -244,21 +288,13 @@ mod tests { let mut expect: Map = Map::new(); expect.insert( - "libp2p".to_string(), - Value::new(None, default.libp2p.collect().unwrap()), - ); - expect.insert( - "rpc_client".to_string(), - Value::new(None, default.rpc_client.collect().unwrap()), + "p2p".to_string(), + Value::new(None, default.p2p.collect().unwrap()), ); expect.insert( "metrics".to_string(), Value::new(None, default.metrics.collect().unwrap()), ); - expect.insert( - "key_store_path".to_string(), - Value::new(None, iroh_data_root().unwrap().to_str()), - ); let got = default.collect().unwrap(); for key in got.keys() { @@ -269,7 +305,7 @@ mod tests { // libp2p let mut expect: Map = Map::new(); - let default = &default.libp2p; + let default = &default.p2p.libp2p; // see `Source` implementation for why we need to cast this as a signed int expect.insert( diff --git a/iroh-p2p/src/main.rs b/iroh-p2p/src/main.rs index 31ee9e4924..61426ae4ba 100644 --- a/iroh-p2p/src/main.rs +++ b/iroh-p2p/src/main.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, Context, Result}; use clap::Parser; use iroh_p2p::config::{Config, CONFIG_FILE_NAME, ENV_PREFIX}; +use iroh_p2p::ServerConfig; use iroh_p2p::{cli::Args, metrics, DiskStorage, Keychain, Node}; use iroh_util::lock::ProgramLock; use iroh_util::{iroh_config_path, make_config}; @@ -30,7 +31,7 @@ fn main() -> Result<()> { let sources = [Some(cfg_path.as_path()), args.cfg.as_deref()]; let network_config = make_config( // default - Config::default_network(), + ServerConfig::default(), // potential config files &sources, // env var prefix for this config @@ -55,6 +56,7 @@ fn main() -> Result<()> { } } + let network_config = Config::from(network_config); let kc = Keychain::::new(network_config.key_store_path.clone()).await?; let rpc_addr = network_config .rpc_addr() diff --git a/iroh-rpc-client/src/config.rs b/iroh-rpc-client/src/config.rs index 850db5dee9..32da0af954 100644 --- a/iroh-rpc-client/src/config.rs +++ b/iroh-rpc-client/src/config.rs @@ -13,6 +13,9 @@ pub struct Config { /// Store rpc address. pub store_addr: Option, /// Number of concurent channels. + /// + /// If `None` defaults to `1`, not used for in-memory addresses. + // TODO: Consider changing this to NonZeroUsize instead of Option. pub channels: Option, } diff --git a/iroh-share/src/p2p_node.rs b/iroh-share/src/p2p_node.rs index 093d73b0dc..ec22d7b403 100644 --- a/iroh-share/src/p2p_node.rs +++ b/iroh-share/src/p2p_node.rs @@ -3,7 +3,7 @@ use std::{collections::HashSet, path::Path, sync::Arc}; use anyhow::{ensure, Result}; use async_trait::async_trait; use cid::Cid; -use iroh_p2p::{config, Keychain, MemoryStorage, NetworkEvent, Node}; +use iroh_p2p::{config, Config, Keychain, MemoryStorage, NetworkEvent, Node}; use iroh_resolver::resolver::Resolver; use iroh_rpc_client::Client; use iroh_rpc_types::Addr; @@ -159,10 +159,9 @@ impl P2pNode { libp2p_config.relay_server = false; libp2p_config.max_conns_in = 8; libp2p_config.max_conns_out = 8; - let config = config::Config { + let config = Config { libp2p: libp2p_config, rpc_client: rpc_p2p_client_config.clone(), - metrics: Default::default(), key_store_path: db_path.parent().unwrap().to_path_buf(), }; @@ -173,10 +172,6 @@ impl P2pNode { let store_config = iroh_store::Config { path: db_path.to_path_buf(), rpc_client: rpc_store_client_config, - metrics: iroh_metrics::config::Config { - tracing: false, // disable tracing by default - ..Default::default() - }, }; let store = if store_config.path.exists() { diff --git a/iroh-store/benches/rpc.rs b/iroh-store/benches/rpc.rs index a0c6aacc78..df9d1164ea 100644 --- a/iroh-store/benches/rpc.rs +++ b/iroh-store/benches/rpc.rs @@ -3,7 +3,6 @@ use std::time::Instant; use bytes::Bytes; use cid::multihash::{Code, MultihashDigest}; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -use iroh_metrics::config::Config as MetricsConfig; use iroh_rpc_client::{Client, Config as RpcClientConfig}; use iroh_rpc_types::{store::StoreAddr, Addr}; use iroh_store::{Config, Store}; @@ -56,15 +55,10 @@ pub fn put_benchmark(c: &mut Criterion) { let executor = Runtime::new().unwrap(); let (server_addr, client_addr, _dir) = transport.new_addr(); let rpc_client = RpcClientConfig { - store_addr: Some(client_addr), + store_addr: Some(client_addr.clone()), ..Default::default() }; - - let config = Config { - path: dir.path().join("db"), - rpc_client: rpc_client.clone(), - metrics: MetricsConfig::default(), - }; + let config = Config::with_rpc_addr(dir.path().join("db"), client_addr); let (_task, rpc) = executor.block_on(async { let store = Store::create(config).await.unwrap(); let task = executor.spawn(async move { @@ -109,15 +103,10 @@ pub fn get_benchmark(c: &mut Criterion) { let dir = tempfile::tempdir().unwrap(); let (server_addr, client_addr, _dir) = transport.new_addr(); let rpc_client = RpcClientConfig { - store_addr: Some(client_addr), + store_addr: Some(client_addr.clone()), ..Default::default() }; - - let config = Config { - path: dir.path().join("db"), - rpc_client: rpc_client.clone(), - metrics: MetricsConfig::default(), - }; + let config = Config::with_rpc_addr(dir.path().join("db"), client_addr); let (_task, rpc) = executor.block_on(async { let store = Store::create(config).await.unwrap(); let task = executor.spawn(async move { diff --git a/iroh-store/benches/store.rs b/iroh-store/benches/store.rs index 54aebb7eda..f5212a8b6c 100644 --- a/iroh-store/benches/store.rs +++ b/iroh-store/benches/store.rs @@ -2,8 +2,6 @@ use std::time::Instant; use cid::multihash::{Code, MultihashDigest}; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -use iroh_metrics::config::Config as MetricsConfig; -use iroh_rpc_client::Config as RpcClientConfig; use iroh_store::{Config, Store}; use tokio::runtime::Runtime; @@ -23,12 +21,7 @@ pub fn put_benchmark(c: &mut Criterion) { |b, (key, value)| { let executor = Runtime::new().unwrap(); let dir = tempfile::tempdir().unwrap(); - let rpc_client = RpcClientConfig::default(); - let config = Config { - path: dir.path().into(), - rpc_client, - metrics: MetricsConfig::default(), - }; + let config = Config::new(dir.path().into()); let store = executor.block_on(async { Store::create(config).await.unwrap() }); let store_ref = &store; b.to_async(&executor) @@ -49,12 +42,7 @@ pub fn get_benchmark(c: &mut Criterion) { |b, _| { let executor = Runtime::new().unwrap(); let dir = tempfile::tempdir().unwrap(); - let rpc_client = RpcClientConfig::default(); - let config = Config { - path: dir.path().into(), - rpc_client, - metrics: MetricsConfig::default(), - }; + let config = Config::new(dir.path().into()); let store = executor.block_on(async { Store::create(config).await.unwrap() }); let store_ref = &store; let keys = executor.block_on(async { diff --git a/iroh-store/src/config.rs b/iroh-store/src/config.rs index a546baa49a..6e3d0bb14c 100644 --- a/iroh-store/src/config.rs +++ b/iroh-store/src/config.rs @@ -23,7 +23,47 @@ pub fn config_data_path(arg_path: Option) -> Result { } } -/// The configuration for the store. +/// The configuration for the store server. +/// +/// This is the configuration which the store server binary needs to run. This is a +/// superset from the configuration needed by the store service, which can also run +/// integrated into another binary like in iroh-one, iroh-share or iroh-embed. +#[derive(PartialEq, Debug, Deserialize, Serialize, Clone)] +pub struct ServerConfig { + /// Configuration of the store service. + pub store: Config, + /// Configuration for metrics export. + pub metrics: MetricsConfig, +} + +impl ServerConfig { + pub fn new(path: PathBuf) -> Self { + let addr = "irpc://0.0.0.0:4402".parse().unwrap(); + Self { + store: Config::with_rpc_addr(path, addr), + metrics: Default::default(), + } + } +} + +impl Source for ServerConfig { + fn clone_into_box(&self) -> Box { + Box::new(self.clone()) + } + + fn collect(&self) -> Result, ConfigError> { + let mut map: Map = Map::new(); + insert_into_config_map(&mut map, "store", self.store.collect()?); + insert_into_config_map(&mut map, "metrics", self.metrics.collect()?); + Ok(map) + } +} + +/// The configuration for the store service. +/// +/// As opposed to the [`ServerConfig`] this is only the configuration needed to run the +/// store service. It can still be deserialised from a file, which is e.g. used by +/// iroh-one. #[derive(PartialEq, Debug, Deserialize, Serialize, Clone)] pub struct Config { /// The location of the content database. @@ -33,26 +73,38 @@ pub struct Config { /// Only used to extract the listening address from the `store_addr` field. // TODO: split off listening address from RpcClientConfig. pub rpc_client: RpcClientConfig, - pub metrics: MetricsConfig, +} + +impl From for Config { + fn from(source: ServerConfig) -> Self { + source.store + } } impl Config { - pub fn new_with_rpc(path: PathBuf, client_addr: StoreAddr) -> Self { + /// Creates a new store config. + /// + /// This config will not have any RpcClientConfig, but that is fine because it is mostly + /// unused: `iroh_rpc::rpc::new` which is used takes the RPC address as a separate + /// argument. Once #672 is merged we can probably remove the `rpc_client` field. + pub fn new(path: PathBuf) -> Self { + Self { + path, + rpc_client: Default::default(), + } + } + + /// Creates a new store config with the given RPC listen address. + pub fn with_rpc_addr(path: PathBuf, addr: StoreAddr) -> Self { Self { path, rpc_client: RpcClientConfig { - store_addr: Some(client_addr), + store_addr: Some(addr), ..Default::default() }, - metrics: MetricsConfig::default(), } } - pub fn new_network(path: PathBuf) -> Self { - let addr = "irpc://0.0.0.0:4402"; - Self::new_with_rpc(path, addr.parse().unwrap()) - } - pub fn rpc_addr(&self) -> Option { self.rpc_client.store_addr.clone() } @@ -62,6 +114,7 @@ impl Source for Config { fn clone_into_box(&self) -> Box { Box::new(self.clone()) } + fn collect(&self) -> Result, ConfigError> { let mut map: Map = Map::new(); let path = self @@ -70,8 +123,6 @@ impl Source for Config { .ok_or_else(|| ConfigError::Foreign("No `path` set. Path is required.".into()))?; insert_into_config_map(&mut map, "path", path); insert_into_config_map(&mut map, "rpc_client", self.rpc_client.collect()?); - insert_into_config_map(&mut map, "metrics", self.metrics.collect()?); - Ok(map) } } @@ -84,16 +135,12 @@ mod tests { #[cfg(unix)] fn test_collect() { let path = PathBuf::new().join("test"); - let default = Config::new_network(path); + let default = ServerConfig::new(path); let mut expect: Map = Map::new(); expect.insert( - "rpc_client".to_string(), - Value::new(None, default.rpc_client.collect().unwrap()), - ); - expect.insert( - "path".to_string(), - Value::new(None, default.path.to_str().unwrap()), + "store".to_string(), + Value::new(None, default.store.collect().unwrap()), ); expect.insert( "metrics".to_string(), @@ -112,8 +159,8 @@ mod tests { #[cfg(unix)] fn test_build_config_from_struct() { let path = PathBuf::new().join("test"); - let expect = Config::new_network(path); - let got: Config = config::Config::builder() + let expect = ServerConfig::new(path); + let got: ServerConfig = config::Config::builder() .add_source(expect.clone()) .build() .unwrap() diff --git a/iroh-store/src/main.rs b/iroh-store/src/main.rs index d8bf03d14e..20a53ab333 100644 --- a/iroh-store/src/main.rs +++ b/iroh-store/src/main.rs @@ -2,8 +2,8 @@ use anyhow::anyhow; use clap::Parser; use iroh_store::{ cli::Args, - config::{config_data_path, CONFIG_FILE_NAME, ENV_PREFIX}, - metrics, rpc, Config, Store, + config::{config_data_path, Config, ServerConfig, CONFIG_FILE_NAME, ENV_PREFIX}, + metrics, rpc, Store, }; use iroh_util::lock::ProgramLock; use iroh_util::{block_until_sigint, iroh_config_path, make_config}; @@ -24,7 +24,7 @@ async fn main() -> anyhow::Result<()> { let config_data_path = config_data_path(args.path.clone())?; let config = make_config( // default - Config::new_network(config_data_path), + ServerConfig::new(config_data_path), // potential config files sources, // env var prefix for this config @@ -49,6 +49,7 @@ async fn main() -> anyhow::Result<()> { } } + let config = Config::from(config); let rpc_addr = config .rpc_addr() .ok_or_else(|| anyhow!("missing store rpc addr"))?; diff --git a/iroh-store/src/store.rs b/iroh-store/src/store.rs index 2ba7c7953b..dc1883351f 100644 --- a/iroh-store/src/store.rs +++ b/iroh-store/src/store.rs @@ -679,11 +679,6 @@ impl<'a> ReadStore<'a> { mod tests { use std::{str::FromStr, sync::Mutex}; - use super::*; - - use iroh_metrics::config::Config as MetricsConfig; - use iroh_rpc_client::Config as RpcClientConfig; - use cid::multihash::{Code, MultihashDigest}; use libipld::{ cbor::DagCborCodec, @@ -691,19 +686,16 @@ mod tests { Ipld, IpldCodec, }; use tempfile::TempDir; + + use super::*; + const RAW: u64 = 0x55; const DAG_CBOR: u64 = 0x71; #[tokio::test] async fn test_basics() { let dir = tempfile::tempdir().unwrap(); - let rpc_client = RpcClientConfig::default(); - let config = Config { - path: dir.path().into(), - rpc_client, - metrics: MetricsConfig::default(), - }; - + let config = Config::new(dir.path().into()); let store = Store::create(config).await.unwrap(); let mut values = Vec::new(); @@ -736,12 +728,7 @@ mod tests { #[tokio::test] async fn test_reopen() { let dir = tempfile::tempdir().unwrap(); - let rpc_client = RpcClientConfig::default(); - let config = Config { - path: dir.path().into(), - rpc_client, - metrics: MetricsConfig::default(), - }; + let config = Config::new(dir.path().into()); let store = Store::create(config.clone()).await.unwrap(); @@ -805,13 +792,7 @@ mod tests { async fn test_store() -> anyhow::Result<(Store, TempDir)> { let dir = tempfile::tempdir()?; - let rpc_client = RpcClientConfig::default(); - let config = Config { - path: dir.path().into(), - rpc_client, - metrics: MetricsConfig::default(), - }; - + let config = Config::new(dir.path().into()); let store = Store::create(config).await?; Ok((store, dir)) }