Skip to content

Commit

Permalink
feat: get config from files, environment vars, & command line flags (#…
Browse files Browse the repository at this point in the history
…112)

Add `make_config` util function that is used to load config
files
`make_config` asks for a:
1) default config
2) a list of possible file paths where a config can be loaded
3) a prefix that this config's env vars will be labeled (eg,
   `IROH_GATEWAY_PORT=4000`, the prefix is `IROH_GATEWAY` & the field
   that you are trying to set is `port`
4) command line flag overrides

The method layers these options, starting with the default config and
ending with the command line flags.

Alos, allows using `IROH_METRICS_*` env vars to set fields in the metrics
config. Also allows for `IROH_INSTANCE_ID` to set
`metrics::Config.instance_id` & `IROH_ENV` to set
`metrics::Config.service_env` fields.
  • Loading branch information
ramfox authored Jun 9, 2022
1 parent b75bbbd commit 652b7ff
Show file tree
Hide file tree
Showing 38 changed files with 1,270 additions and 269 deletions.
2 changes: 1 addition & 1 deletion examples/src/bin/importer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use clap::Parser;
use futures::{stream::TryStreamExt, StreamExt};
use indicatif::{ProgressBar, ProgressStyle};
use iroh_car::CarReader;
use iroh_rpc_client::{Client, RpcClientConfig};
use iroh_rpc_client::{Client, Config as RpcClientConfig};
use par_stream::prelude::*;

#[derive(Parser, Debug, Clone)]
Expand Down
22 changes: 5 additions & 17 deletions iroh-bitswap/src/metrics.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
use git_version::git_version;
use iroh_metrics::config::Config as MetricsConfig;

pub fn metrics_config(logger_only: bool) -> iroh_metrics::config::Config {
pub fn metrics_config_with_compile_time_info(cfg: MetricsConfig) -> MetricsConfig {
// compile time configuration
let service_name = env!("CARGO_PKG_NAME").to_string();
let build = git_version!().to_string();
let version = env!("CARGO_PKG_VERSION").to_string();

// runtime configuration
let instance_id = std::env::var("IROH_INSTANCE_ID")
.unwrap_or_else(|_| names::Generator::default().next().unwrap());
let service_env = std::env::var("IROH_ENV").unwrap_or_else(|_| "dev".to_string());
iroh_metrics::config::Config::new(
service_name,
instance_id,
build,
version,
service_env,
logger_only,
)
cfg.with_service_name(env!("CARGO_PKG_NAME").to_string())
.with_build(git_version!().to_string())
.with_version(env!("CARGO_PKG_VERSION").to_string())
}
6 changes: 6 additions & 0 deletions iroh-ctl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ clap = { version = "3.1.14", features = ["derive"] }
crossterm = "0.23.2"
tonic = "0.7.2"
iroh-rpc-client = { path = "../iroh-rpc-client" }
config = "0.13.1"
iroh-util = { path = "../iroh-util" }
serde = { version = "1.0", features = ["derive"] }
git-version = "0.3.5"
iroh-metrics = { path = "../iroh-metrics" }
prometheus-client = "0.16.0"
69 changes: 69 additions & 0 deletions iroh-ctl/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use config::{ConfigError, Map, Source, Value};
use iroh_metrics::config::Config as MetricsConfig;
use iroh_rpc_client::Config as RpcClientConfig;
use iroh_util::insert_into_config_map;
use serde::{Deserialize, Serialize};

/// CONFIG_FILE_NAME is the name of the optional config file located in the iroh home directory
pub const CONFIG_FILE_NAME: &str = "ctl.config.toml";
/// ENV_PREFIX should be used along side the config field name to set a config field using
/// environment variables
pub const ENV_PREFIX: &str = "IROH_CTL";

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Config {
pub rpc_client: RpcClientConfig,
pub metrics: MetricsConfig,
}

impl Source for Config {
fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
Box::new(self.clone())
}
fn collect(&self) -> Result<Map<String, Value>, ConfigError> {
let mut map: Map<String, Value> = Map::new();
insert_into_config_map(&mut map, "rpc_client", self.rpc_client.collect()?);
insert_into_config_map(&mut map, "metrics", self.metrics.collect()?);
Ok(map)
}
}

#[cfg(test)]
mod tests {
use super::*;
use config::Config as ConfigBuilder;

#[test]
fn test_collect() {
let default = Config::default();
let mut expect: Map<String, Value> = Map::new();
expect.insert(
"rpc_client".to_string(),
Value::new(None, default.rpc_client.collect().unwrap()),
);
expect.insert(
"metrics".to_string(),
Value::new(None, default.metrics.collect().unwrap()),
);
let got = default.collect().unwrap();

for key in got.keys() {
let left = expect.get(key).unwrap();
let right = got.get(key).unwrap();
assert_eq!(left, right);
}
}

#[test]
fn test_build_config_from_struct() {
let expect = Config::default();
let got: Config = ConfigBuilder::builder()
.add_source(expect.clone())
.build()
.unwrap()
.try_deserialize()
.unwrap();

assert_eq!(expect, got);
}
}
2 changes: 2 additions & 0 deletions iroh-ctl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
pub mod config;
pub mod metrics;
pub mod status;
54 changes: 52 additions & 2 deletions iroh-ctl/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
use std::collections::HashMap;
use std::path::PathBuf;

use clap::{Parser, Subcommand};
use iroh_ctl::metrics;
use iroh_rpc_client::Client;
use iroh_util::{iroh_home_path, make_config};
use prometheus_client::registry::Registry;

use iroh_ctl::status;
use iroh_ctl::{
config::{Config, CONFIG_FILE_NAME, ENV_PREFIX},
status,
};

#[derive(Parser, Debug, Clone)]
#[clap(author, version, about, long_about = None, propagate_version = true)]
struct Cli {
#[clap(long)]
cfg: Option<PathBuf>,
#[clap(long = "no-metrics")]
no_metrics: bool,
#[clap(subcommand)]
command: Commands,
}

impl Cli {
fn make_overrides_map(&self) -> HashMap<String, String> {
let mut map = HashMap::new();
map.insert("metrics.debug".to_string(), self.no_metrics.to_string());
map
}
}

#[derive(Subcommand, Debug, Clone)]
enum Commands {
/// status checks the health of the differen processes
Expand All @@ -23,11 +45,39 @@ enum Commands {
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();

let sources = vec![iroh_home_path(CONFIG_FILE_NAME), cli.cfg.clone()];
let config = make_config(
// default
Config::default(),
// potential config files
sources,
// env var prefix for this config
ENV_PREFIX,
// map of present command line arguments
cli.make_overrides_map(),
)
.unwrap();

let metrics_config = config.metrics.clone();

// stubbing in metrics
let prom_registry = Registry::default();
// TODO: need to register prometheus metrics
let metrics_handle = iroh_metrics::init_with_registry(
metrics::metrics_config_with_compile_time_info(metrics_config),
prom_registry,
)
.await
.expect("failed to initialize metrics");

let client = Client::new(&config.rpc_client).await?;

match cli.command {
Commands::Status { watch } => {
crate::status::status(watch).await?;
crate::status::status(client, watch).await?;
}
};

metrics_handle.shutdown();
Ok(())
}
9 changes: 9 additions & 0 deletions iroh-ctl/src/metrics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use git_version::git_version;
use iroh_metrics::config::Config as MetricsConfig;

pub fn metrics_config_with_compile_time_info(cfg: MetricsConfig) -> MetricsConfig {
// compile time configuration
cfg.with_service_name(env!("CARGO_PKG_NAME").to_string())
.with_build(git_version!().to_string())
.with_version(env!("CARGO_PKG_VERSION").to_string())
}
6 changes: 2 additions & 4 deletions iroh-ctl/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ use anyhow::Result;
use crossterm::terminal::{Clear, ClearType};
use crossterm::{cursor, style, style::Stylize, QueueableCommand};
use futures::StreamExt;
use iroh_rpc_client::{Client, RpcClientConfig, ServiceStatus, StatusRow, StatusTable};

pub async fn status(watch: bool) -> Result<()> {
let client = Client::new(&RpcClientConfig::default()).await.unwrap();
use iroh_rpc_client::{Client, ServiceStatus, StatusRow, StatusTable};

pub async fn status(client: Client, watch: bool) -> Result<()> {
let mut stdout = stdout();
if watch {
let status_stream = client.watch().await;
Expand Down
4 changes: 4 additions & 0 deletions iroh-gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ async-recursion = "1.0.0"
handlebars = "3"
url = "2.2.2"
urlencoding = "2.1.0"
dirs = "4.0.0"
toml = "0.5.9"
http-serde = "1.1.0"
config = "0.13.1"

[dev-dependencies]
axum-macros = "0.2.0" # use #[axum_macros::debug_handler] for better error messages on handlers
Loading

0 comments on commit 652b7ff

Please sign in to comment.