Node envconfig (#382)

* Move config to a new location

* Log config override with env variables

* Network config override with env variables

* Http env vars

* Consensus env vars
This commit is contained in:
gusto 2023-09-11 14:56:35 +03:00 committed by GitHub
parent ea64b74aa6
commit 8e0937beb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 338 additions and 46 deletions

View File

@ -7,6 +7,7 @@ consensus:
fountain_settings: null
overlay_settings:
nodes: [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]
leader_super_majority_threshold: 1
leader:
cur: 0
network:
@ -14,7 +15,7 @@ network:
host: 0.0.0.0
port: 3000
log_level: "fatal"
nodeKey: null
node_key: "0000000000000000000000000000000000000000000000000000000000000001"
discV5BootstrapNodes: []
initial_peers: []
relayTopics: []

View File

@ -0,0 +1,300 @@
use std::{
net::{IpAddr, SocketAddr},
path::PathBuf,
time::Duration,
};
use crate::Carnot;
use clap::{Parser, ValueEnum};
use color_eyre::eyre::{self, eyre, Result};
use hex::FromHex;
#[cfg(feature = "metrics")]
use metrics::{backend::map::MapMetricsBackend, types::MetricsData, MetricsService};
use nomos_http::{backends::axum::AxumBackend, http::HttpService};
#[cfg(feature = "libp2p")]
use nomos_libp2p::{secp256k1::SecretKey, Multiaddr};
use nomos_log::{Logger, LoggerBackend, LoggerFormat};
#[cfg(feature = "libp2p")]
use nomos_network::backends::libp2p::Libp2p;
#[cfg(feature = "waku")]
use nomos_network::backends::waku::Waku;
use nomos_network::NetworkService;
use overwatch_rs::services::ServiceData;
use serde::{Deserialize, Serialize};
use tracing::Level;
#[cfg(feature = "waku")]
use waku_bindings::{Multiaddr, SecretKey};
#[derive(ValueEnum, Clone, Debug, Default)]
pub enum LoggerBackendType {
Gelf,
File,
#[default]
Stdout,
Stderr,
}
#[derive(Parser, Debug, Clone)]
pub struct LogArgs {
/// Address for the Gelf backend
#[clap(long = "log-addr", env = "LOG_ADDR", required_if_eq("backend", "Gelf"))]
log_addr: Option<SocketAddr>,
/// Directory for the File backend
#[clap(long = "log-dir", env = "LOG_DIR", required_if_eq("backend", "File"))]
directory: Option<PathBuf>,
/// Prefix for the File backend
#[clap(long = "log-path", env = "LOG_PATH", required_if_eq("backend", "File"))]
prefix: Option<PathBuf>,
/// Backend type
#[clap(long = "log-backend", env = "LOG_BACKEND", value_enum)]
backend: Option<LoggerBackendType>,
#[clap(long = "log-format", env = "LOG_FORMAT")]
format: Option<String>,
#[clap(long = "log-level", env = "LOG_LEVEL")]
level: Option<String>,
}
#[derive(Parser, Debug, Clone)]
pub struct NetworkArgs {
#[clap(long = "net-host", env = "NET_HOST")]
host: Option<IpAddr>,
#[clap(long = "net-port", env = "NET_PORT")]
port: Option<usize>,
#[clap(long = "net-node-key", env = "NET_NODE_KEY")]
node_key: Option<String>,
#[clap(long = "net-initial-peers", env = "NET_INITIAL_PEERS")]
pub initial_peers: Option<Vec<Multiaddr>>,
}
#[derive(Parser, Debug, Clone)]
pub struct HttpArgs {
#[clap(long = "http-host", env = "HTTP_HOST")]
http_addr: Option<SocketAddr>,
#[clap(long = "http-cors-origin", env = "HTTP_CORS_ORIGIN")]
pub cors_origins: Option<Vec<String>>,
}
#[derive(Parser, Debug, Clone)]
pub struct ConsensusArgs {
#[clap(long = "consensus-priv-key", env = "CONSENSUS_PRIV_KEY")]
consensus_priv_key: Option<String>,
#[clap(long = "consensus-timeout-secs", env = "CONSENSUS_TIMEOUT_SECS")]
consensus_timeout_secs: Option<String>,
}
#[derive(ValueEnum, Clone, Debug, Default)]
pub enum OverlayType {
#[default]
Flat,
Tree,
}
#[derive(Parser, Debug, Clone)]
pub struct OverlayArgs {
// TODO: Act on type and support other overlays.
#[clap(long = "overlay-type", env = "OVERLAY_TYPE")]
pub overlay_type: Option<OverlayType>,
#[clap(long = "overlay-nodes", env = "OVERLAY_NODES")]
pub overlay_nodes: Option<Vec<String>>,
#[clap(long = "overlay-leader", env = "OVERLAY_LEADER")]
pub overlay_leader: Option<usize>,
#[clap(
long = "overlay-leader-super-majority-threshold",
env = "OVERLAY_LEADER_SUPER_MAJORITY_THRESHOLD"
)]
pub overlay_leader_super_majority_threshold: Option<usize>,
}
#[derive(Deserialize, Debug, Clone, Serialize)]
pub struct Config {
pub log: <Logger as ServiceData>::Settings,
#[cfg(feature = "waku")]
pub network: <NetworkService<Waku> as ServiceData>::Settings,
#[cfg(feature = "libp2p")]
pub network: <NetworkService<Libp2p> as ServiceData>::Settings,
pub http: <HttpService<AxumBackend> as ServiceData>::Settings,
pub consensus: <Carnot as ServiceData>::Settings,
#[cfg(feature = "metrics")]
pub metrics: <MetricsService<MapMetricsBackend<MetricsData>> as ServiceData>::Settings,
}
impl Config {
pub fn update_log(mut self, log_args: LogArgs) -> Result<Self> {
let LogArgs {
backend,
log_addr: addr,
directory,
prefix,
format,
level,
} = log_args;
// Override the file config with the one from env variables.
if let Some(backend) = backend {
self.log.backend = match backend {
LoggerBackendType::Gelf => LoggerBackend::Gelf {
addr: addr.ok_or_else(|| eyre!("Gelf backend requires an address."))?,
},
LoggerBackendType::File => LoggerBackend::File {
directory: directory
.ok_or_else(|| eyre!("File backend requires a directory."))?,
prefix,
},
LoggerBackendType::Stdout => LoggerBackend::Stdout,
LoggerBackendType::Stderr => LoggerBackend::Stderr,
}
};
// Update parts of the config.
if let Some(format_str) = format {
self.log.format = match format_str.as_str() {
"Json" => LoggerFormat::Json,
"Plain" => LoggerFormat::Plain,
_ => return Err(eyre!("Invalid log format provided.")),
};
}
if let Some(level_str) = level {
self.log.level = match level_str.as_str() {
"DEBUG" => Level::DEBUG,
_ => return Err(eyre!("Invalid log level provided.")),
};
}
Ok(self)
}
#[cfg(feature = "waku")]
pub fn update_network(mut self, network_args: NetworkArgs) -> Result<Self> {
let NetworkArgs {
host,
port,
node_key,
initial_peers,
} = network_args;
if let Some(host) = host {
self.network.backend.inner.host = Some(host);
}
if let Some(port) = port {
self.network.backend.inner.port = Some(port);
}
if let Some(node_key) = node_key {
use std::str::FromStr;
self.network.backend.inner.node_key = Some(SecretKey::from_str(&node_key)?);
}
if let Some(peers) = initial_peers {
self.network.backend.initial_peers = peers;
}
Ok(self)
}
#[cfg(feature = "libp2p")]
pub fn update_network(mut self, network_args: NetworkArgs) -> Result<Self> {
let NetworkArgs {
host,
port,
node_key,
initial_peers,
} = network_args;
if let Some(IpAddr::V4(h)) = host {
self.network.backend.host = h;
} else if host.is_some() {
return Err(eyre!("Unsupported ip version"));
}
if let Some(port) = port {
self.network.backend.port = port as u16;
}
if let Some(node_key) = node_key {
let mut key_bytes = hex::decode(node_key)?;
self.network.backend.node_key = SecretKey::try_from_bytes(key_bytes.as_mut_slice())?;
}
if let Some(peers) = initial_peers {
self.network.backend.initial_peers = peers;
}
Ok(self)
}
pub fn update_http(mut self, http_args: HttpArgs) -> Result<Self> {
let HttpArgs {
http_addr,
cors_origins,
} = http_args;
if let Some(addr) = http_addr {
self.http.backend.address = addr;
}
if let Some(cors) = cors_origins {
self.http.backend.cors_origins = cors;
}
Ok(self)
}
pub fn update_consensus(mut self, consensus_args: ConsensusArgs) -> Result<Self> {
let ConsensusArgs {
consensus_priv_key,
consensus_timeout_secs,
} = consensus_args;
if let Some(private_key) = consensus_priv_key {
let bytes = <[u8; 32]>::from_hex(private_key)?;
self.consensus.private_key = bytes;
}
if let Some(timeout) = consensus_timeout_secs {
let secs = timeout.parse::<u64>()?;
self.consensus.timeout = Duration::from_secs(secs);
}
Ok(self)
}
pub fn update_overlay(mut self, overlay_args: OverlayArgs) -> Result<Self> {
let OverlayArgs {
overlay_nodes,
overlay_leader_super_majority_threshold,
..
} = overlay_args;
if let Some(nodes) = overlay_nodes {
self.consensus.overlay_settings.nodes = nodes
.iter()
.map(|n| {
<[u8; 32]>::from_hex(n)
.map_err(|e| eyre::eyre!("Failed to decode hex: {}", e))
.map(|b| b.into())
})
.collect::<Result<Vec<_>, eyre::Report>>()?;
}
if let Some(threshold) = overlay_leader_super_majority_threshold {
self.consensus
.overlay_settings
.leader_super_majority_threshold = Some(threshold.into());
}
Ok(self)
}
}

View File

@ -1,4 +1,5 @@
mod blob;
mod config;
mod tx;
use color_eyre::eyre::Result;
@ -14,8 +15,6 @@ use nomos_core::fountain::mock::MockFountain;
use nomos_http::backends::axum::AxumBackend;
use nomos_http::bridge::HttpBridgeService;
use nomos_http::http::HttpService;
#[cfg(feature = "libp2p")]
use nomos_libp2p::secp256k1::SecretKey;
use nomos_log::Logger;
#[cfg(feature = "libp2p")]
use nomos_mempool::network::adapters::libp2p::Libp2pAdapter as MempoolLibp2pAdapter;
@ -28,49 +27,15 @@ use nomos_network::backends::libp2p::Libp2p;
use nomos_network::backends::waku::Waku;
use nomos_network::NetworkService;
use overwatch_derive::*;
use overwatch_rs::services::{handle::ServiceHandle, ServiceData};
use serde::{Deserialize, Serialize};
#[cfg(feature = "waku")]
use waku_bindings::SecretKey;
use overwatch_rs::services::handle::ServiceHandle;
use crate::blob::Blob;
pub use config::{Config, ConsensusArgs, HttpArgs, LogArgs, NetworkArgs, OverlayArgs};
pub use tx::Tx;
#[cfg(all(feature = "waku", feature = "libp2p"))]
compile_error!("feature \"waku\" and feature \"libp2p\" cannot be enabled at the same time");
#[derive(Deserialize, Debug, Clone, Serialize)]
pub struct Config {
pub log: <Logger as ServiceData>::Settings,
#[cfg(feature = "waku")]
pub network: <NetworkService<Waku> as ServiceData>::Settings,
#[cfg(feature = "libp2p")]
pub network: <NetworkService<Libp2p> as ServiceData>::Settings,
pub http: <HttpService<AxumBackend> as ServiceData>::Settings,
pub consensus: <Carnot as ServiceData>::Settings,
#[cfg(feature = "metrics")]
pub metrics: <MetricsService<MapMetricsBackend<MetricsData>> as ServiceData>::Settings,
}
impl Config {
pub fn set_node_key(&mut self, node_key: Option<String>) -> Result<()> {
if let Some(node_key) = node_key {
#[cfg(feature = "waku")]
{
use std::str::FromStr;
self.network.backend.inner.node_key = Some(SecretKey::from_str(&node_key)?)
}
#[cfg(feature = "libp2p")]
{
let mut key_bytes = hex::decode(node_key)?;
self.network.backend.node_key =
SecretKey::try_from_bytes(key_bytes.as_mut_slice())?;
}
}
Ok(())
}
}
#[cfg(feature = "waku")]
pub type Carnot = CarnotConsensus<
ConsensusWakuAdapter,

View File

@ -1,4 +1,7 @@
use nomos_node::{Config, Nomos, NomosServiceSettings, Tx};
use nomos_node::{
Config, ConsensusArgs, HttpArgs, LogArgs, NetworkArgs, Nomos, NomosServiceSettings,
OverlayArgs, Tx,
};
mod bridges;
@ -22,15 +25,38 @@ use std::sync::Arc;
struct Args {
/// Path for a yaml-encoded network config file
config: std::path::PathBuf,
/// Overrides node key in config file
#[clap(long = "node-key")]
node_key: Option<String>,
/// Overrides log config.
#[clap(flatten)]
log_args: LogArgs,
/// Overrides network config.
#[clap(flatten)]
network_args: NetworkArgs,
/// Overrides http config.
#[clap(flatten)]
http_args: HttpArgs,
/// Overrides consensus config.
#[clap(flatten)]
consensus_args: ConsensusArgs,
/// Overrides overlay config.
#[clap(flatten)]
overlay_args: OverlayArgs,
}
fn main() -> Result<()> {
let Args { config, node_key } = Args::parse();
let mut config = serde_yaml::from_reader::<_, Config>(std::fs::File::open(config)?)?;
config.set_node_key(node_key)?;
let Args {
config,
log_args,
http_args,
network_args,
consensus_args,
overlay_args,
} = Args::parse();
let config = serde_yaml::from_reader::<_, Config>(std::fs::File::open(config)?)?
.update_log(log_args)?
.update_http(http_args)?
.update_consensus(consensus_args)?
.update_overlay(overlay_args)?
.update_network(network_args)?;
let bridges: Vec<HttpBridge> = vec![
Arc::new(Box::new(bridges::carnot_info_bridge)),