From 124c95ae98e4a753ade46cb375fb616834eb6f51 Mon Sep 17 00:00:00 2001 From: Carlos V Date: Mon, 7 Oct 2024 20:06:35 +0000 Subject: [PATCH] fix: Accept config to be through file or env vars --- config/src/config.rs | 34 ++++++++++++++++++---------------- service/src/cli.rs | 2 +- service/src/service.rs | 7 ++++--- tap-agent/src/config.rs | 13 +++++++++++-- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/config/src/config.rs b/config/src/config.rs index 1940c32e..248132e3 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -70,17 +70,19 @@ impl ConfigPrefix { } impl Config { - pub fn parse(prefix: ConfigPrefix, filename: &PathBuf) -> Result { + pub fn parse(prefix: ConfigPrefix, filename: Option<&PathBuf>) -> Result { let config_defaults = include_str!("../default_values.toml"); - let mut config_content = std::fs::read_to_string(filename) - .map_err(|e| format!("Failed to read config file: {}", e))?; + let mut figment_config = Figment::new().merge(Toml::string(config_defaults)); - config_content = Self::substitute_env_vars(config_content)?; + if let Some(path) = filename { + let mut config_content = std::fs::read_to_string(path) + .map_err(|e| format!("Failed to read config file: {}", e))?; + config_content = Self::substitute_env_vars(config_content)?; + figment_config = figment_config.merge(Toml::string(&config_content)); + } - let config: ConfigWrapper = Figment::new() - .merge(Toml::string(config_defaults)) - .merge(Toml::string(&config_content)) + let config: ConfigWrapper = figment_config .merge(Env::prefixed(prefix.get_prefix()).split("__")) .extract() .map_err(|e| e.to_string())?; @@ -386,7 +388,7 @@ mod tests { fn test_minimal_config() { Config::parse( ConfigPrefix::Service, - &PathBuf::from("minimal-config-example.toml"), + Some(PathBuf::from("minimal-config-example.toml")).as_ref(), ) .unwrap(); } @@ -396,7 +398,7 @@ mod tests { // Generate full config by deserializing the minimal config and let the code fill in the defaults. let max_config = Config::parse( ConfigPrefix::Service, - &PathBuf::from("minimal-config-example.toml"), + Some(PathBuf::from("minimal-config-example.toml")).as_ref(), ) .unwrap(); let max_config_file: Config = toml::from_str( @@ -418,7 +420,7 @@ mod tests { Config::parse( ConfigPrefix::Service, - &PathBuf::from("minimal-config-example.toml"), + Some(PathBuf::from("minimal-config-example.toml")).as_ref(), ) .unwrap(); @@ -458,7 +460,7 @@ mod tests { // This should fail because the subgraphs.network.query_url field is missing Config::parse( ConfigPrefix::Service, - &PathBuf::from(temp_minimal_config_path.path()), + Some(PathBuf::from(temp_minimal_config_path.path())).as_ref(), ) .unwrap_err(); @@ -467,7 +469,7 @@ mod tests { let config = Config::parse( ConfigPrefix::Service, - &PathBuf::from(temp_minimal_config_path.path()), + Some(PathBuf::from(temp_minimal_config_path.path())).as_ref(), ) .unwrap(); @@ -485,7 +487,7 @@ mod tests { let config = Config::parse( ConfigPrefix::Service, - &PathBuf::from("minimal-config-example.toml"), + Some(PathBuf::from("minimal-config-example.toml")).as_ref(), ) .unwrap(); @@ -569,7 +571,7 @@ mod tests { // This should fail because the QUERY_URL env variable is not setup Config::parse( ConfigPrefix::Service, - &PathBuf::from(temp_minimal_config_path.path()), + Some(PathBuf::from(temp_minimal_config_path.path())).as_ref(), ) .unwrap_err(); @@ -578,7 +580,7 @@ mod tests { let config = Config::parse( ConfigPrefix::Service, - &PathBuf::from(temp_minimal_config_path.path()), + Some(PathBuf::from(temp_minimal_config_path.path())).as_ref(), ) .unwrap(); @@ -663,7 +665,7 @@ mod tests { // Parse the config with new datbase vars let config = Config::parse( ConfigPrefix::Service, - &PathBuf::from(temp_minimal_config_path.path()), + Some(PathBuf::from(temp_minimal_config_path.path())).as_ref(), ) .unwrap(); diff --git a/service/src/cli.rs b/service/src/cli.rs index 5b76174d..557e151f 100644 --- a/service/src/cli.rs +++ b/service/src/cli.rs @@ -10,5 +10,5 @@ pub struct Cli { /// Path to the configuration file. /// See https://github.com/graphprotocol/indexer-rs/tree/main/service for examples. #[arg(long, value_name = "FILE", verbatim_doc_comment)] - pub config: PathBuf, + pub config: Option, } diff --git a/service/src/service.rs b/service/src/service.rs index 1203f85c..5ebff0ef 100644 --- a/service/src/service.rs +++ b/service/src/service.rs @@ -130,10 +130,11 @@ pub async fn run() -> anyhow::Result<()> { // general configuration options for any indexer service and specific // options added for JSON-RPC let config = - MainConfig::parse(indexer_config::ConfigPrefix::Service, &cli.config).map_err(|e| { + MainConfig::parse(indexer_config::ConfigPrefix::Service, cli.config.as_ref()).map_err(|e| { error!( - "Invalid configuration file `{}`: {}", - cli.config.display(), + "Invalid configuration file `{}`: {}, if a value is missing you can also use \ + --config to fill the rest of the values", + cli.config.unwrap_or_default().display(), e ); anyhow!(e) diff --git a/tap-agent/src/config.rs b/tap-agent/src/config.rs index 2379de83..84dff3b4 100644 --- a/tap-agent/src/config.rs +++ b/tap-agent/src/config.rs @@ -4,6 +4,7 @@ use clap::Parser; use indexer_config::{Config as IndexerConfig, ConfigPrefix}; use reqwest::Url; +use tracing::error; use std::path::PathBuf; use std::{collections::HashMap, str::FromStr}; @@ -17,7 +18,7 @@ pub struct Cli { /// Path to the configuration file. /// See https://github.com/graphprotocol/indexer-rs/tree/main/tap-agent for examples. #[arg(long, value_name = "FILE", verbatim_doc_comment)] - pub config: PathBuf, + pub config: Option, } impl From for Config { @@ -182,7 +183,15 @@ impl Config { pub fn from_cli() -> Result { let cli = Cli::parse(); let indexer_config = - IndexerConfig::parse(ConfigPrefix::Tap, &cli.config).map_err(|e| anyhow::anyhow!(e))?; + IndexerConfig::parse(ConfigPrefix::Tap, cli.config.as_ref()).map_err(|e| { + error!( + "Invalid configuration file `{}`: {}, if a value is missing you can also use \ + --config to fill the rest of the values", + cli.config.unwrap_or_default().display(), + e + ); + anyhow::anyhow!(e) + })?; let config: Config = indexer_config.into(); // Enables tracing under RUST_LOG variable