Skip to content

Commit c444477

Browse files
committed
feat(config): configuration override using env vars
added log filter to env vars
1 parent c064cc5 commit c444477

File tree

6 files changed

+181
-14
lines changed

6 files changed

+181
-14
lines changed

Cargo.lock

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/router/src/main.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,12 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
55

66
#[ntex::main]
77
async fn main() -> Result<(), Box<dyn std::error::Error>> {
8-
router_entrypoint().await
8+
match router_entrypoint().await {
9+
Ok(_) => Ok(()),
10+
Err(err) => {
11+
eprintln!("Failed to start Hive Router:\n {}", err);
12+
13+
Err(err)
14+
}
15+
}
916
}

lib/router-config/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ thiserror = { workspace = true }
2222
http = { workspace = true }
2323
jsonwebtoken = { workspace = true }
2424
retry-policies = { workspace = true}
25+
tracing = { workspace = true }
2526

2627
schemars = "1.0.4"
2728
humantime-serde = "1.1.1"
2829
config = { version = "0.15.14", features = ["yaml", "json", "json5"] }
30+
envconfig = "0.11.0"
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use config::{builder::BuilderState, ConfigBuilder, ConfigError};
2+
use envconfig::Envconfig;
3+
use tracing::debug;
4+
5+
use crate::log::{LogFormat, LogLevel};
6+
7+
#[derive(Envconfig)]
8+
pub struct EnvVarOverrides {
9+
// Logger overrides
10+
#[envconfig(from = "LOG_LEVEL")]
11+
pub log_level: Option<LogLevel>,
12+
#[envconfig(from = "LOG_FORMAT")]
13+
pub log_format: Option<LogFormat>,
14+
#[envconfig(from = "LOG_FILTER")]
15+
pub log_filter: Option<String>,
16+
17+
// HTTP overrides
18+
#[envconfig(from = "PORT")]
19+
pub http_port: Option<u64>,
20+
#[envconfig(from = "HOST")]
21+
pub http_host: Option<String>,
22+
23+
// Supergraph overrides
24+
#[envconfig(from = "SUPERGRAPH_FILE_PATH")]
25+
pub supergraph_file_path: Option<String>,
26+
#[envconfig(from = "HIVE_CDN_ENDPOINT")]
27+
pub hive_console_cdn_endpoint: Option<String>,
28+
#[envconfig(from = "HIVE_CDN_KEY")]
29+
pub hive_console_cdn_key: Option<String>,
30+
}
31+
32+
#[derive(Debug, thiserror::Error)]
33+
pub enum EnvVarOverridesError {
34+
#[error("Failed to override configuration: {0}")]
35+
FailedToOverrideConfig(#[from] ConfigError),
36+
#[error("Cannot override supergraph source due to conflict: SUPERGRAPH_FILE_PATH and HIVE_CDN_ENDPOINT cannot be used together")]
37+
ConflictingSupergraphSource,
38+
#[error("Missing required environment variable: {0}")]
39+
MissingRequiredEnvVar(&'static str),
40+
}
41+
42+
impl EnvVarOverrides {
43+
pub fn apply_overrides<T: BuilderState>(
44+
mut self,
45+
mut config: ConfigBuilder<T>,
46+
) -> Result<ConfigBuilder<T>, EnvVarOverridesError> {
47+
if let Some(log_level) = self.log_level.take() {
48+
debug!("[config-override] 'log.level' = {:?}", log_level);
49+
config = config.set_override("log.level", log_level.as_str())?;
50+
}
51+
if let Some(log_format) = self.log_format.take() {
52+
debug!("[config-override] 'log.format' = {:?}", log_format);
53+
config = config.set_override("log.format", log_format.as_str())?;
54+
}
55+
if let Some(log_filter) = self.log_filter.take() {
56+
debug!("[config-override] 'log.filter' = {:?}", log_filter);
57+
config = config.set_override("log.filter", log_filter)?;
58+
}
59+
60+
if let Some(http_port) = self.http_port.take() {
61+
debug!("[config-override] 'http.port' = {}", http_port);
62+
config = config.set_override("http.port", http_port)?;
63+
}
64+
65+
if let Some(http_host) = self.http_host.take() {
66+
debug!("[config-override] 'http.host' = {}", http_host);
67+
config = config.set_override("http.host", http_host)?;
68+
}
69+
70+
if self.supergraph_file_path.is_some() && self.hive_console_cdn_endpoint.is_some() {
71+
return Err(EnvVarOverridesError::ConflictingSupergraphSource);
72+
}
73+
74+
if let Some(supergraph_file_path) = self.supergraph_file_path.take() {
75+
config = config.set_override("supergraph.source", "file")?;
76+
config = config.set_override("supergraph.path", supergraph_file_path)?;
77+
}
78+
79+
if let Some(hive_console_cdn_endpoint) = self.hive_console_cdn_endpoint.take() {
80+
config = config.set_override("supergraph.source", "hive")?;
81+
config = config.set_override("supergraph.endpoint", hive_console_cdn_endpoint)?;
82+
83+
if let Some(hive_console_cdn_key) = self.hive_console_cdn_key.take() {
84+
config = config.set_override("supergraph.key", hive_console_cdn_key)?;
85+
} else {
86+
return Err(EnvVarOverridesError::MissingRequiredEnvVar("HIVE_CDN_KEY"));
87+
}
88+
}
89+
90+
Ok(config)
91+
}
92+
}

lib/router-config/src/lib.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod cors;
22
pub mod csrf;
3+
mod env_overrides;
34
pub mod headers;
45
pub mod http_server;
56
pub mod jwt_auth;
@@ -10,14 +11,19 @@ pub mod query_planner;
1011
pub mod supergraph;
1112
pub mod traffic_shaping;
1213

13-
use config::{Config, Environment, File, FileFormat, FileSourceFile};
14+
use config::{Config, File, FileFormat, FileSourceFile};
15+
use envconfig::Envconfig;
1416
use schemars::JsonSchema;
1517
use serde::{Deserialize, Serialize};
1618
use std::path::PathBuf;
1719

1820
use crate::{
19-
http_server::HttpServerConfig, log::LoggingConfig, primitives::file_path::with_start_path,
20-
query_planner::QueryPlannerConfig, supergraph::SupergraphSource,
21+
env_overrides::{EnvVarOverrides, EnvVarOverridesError},
22+
http_server::HttpServerConfig,
23+
log::LoggingConfig,
24+
primitives::file_path::with_start_path,
25+
query_planner::QueryPlannerConfig,
26+
supergraph::SupergraphSource,
2127
traffic_shaping::TrafficShapingExecutorConfig,
2228
};
2329

@@ -74,7 +80,9 @@ pub struct HiveRouterConfig {
7480
#[derive(Debug, thiserror::Error)]
7581
pub enum RouterConfigError {
7682
#[error("Failed to load configuration: {0}")]
77-
ConfigLoadError(config::ConfigError),
83+
ConfigLoadError(#[from] config::ConfigError),
84+
#[error("Failed to apply configuration overrides: {0}")]
85+
EnvVarOverridesError(#[from] EnvVarOverridesError),
7886
}
7987

8088
static DEFAULT_FILE_NAMES: &[&str] = &[
@@ -86,7 +94,8 @@ static DEFAULT_FILE_NAMES: &[&str] = &[
8694

8795
pub fn load_config(
8896
overide_config_path: Option<String>,
89-
) -> Result<HiveRouterConfig, config::ConfigError> {
97+
) -> Result<HiveRouterConfig, RouterConfigError> {
98+
let env_overrides = EnvVarOverrides::init_from_env().expect("failed to init env overrides");
9099
let mut config = Config::builder();
91100
let mut config_root_path = std::env::current_dir().expect("failed to get current directory");
92101

@@ -106,15 +115,10 @@ pub fn load_config(
106115
}
107116
}
108117

118+
config = env_overrides.apply_overrides(config)?;
119+
109120
let mut base_cfg = with_start_path(&config_root_path, || {
110-
config
111-
.add_source(
112-
Environment::with_prefix("HIVE")
113-
.separator("__")
114-
.prefix_separator("__"),
115-
)
116-
.build()?
117-
.try_deserialize::<HiveRouterConfig>()
121+
config.build()?.try_deserialize::<HiveRouterConfig>()
118122
})?;
119123

120124
base_cfg.root_directory = config_root_path;

lib/router-config/src/log.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::str::FromStr;
2+
13
use schemars::JsonSchema;
24
use serde::{Deserialize, Serialize};
35

@@ -28,6 +30,21 @@ pub enum LogLevel {
2830
Error,
2931
}
3032

33+
impl FromStr for LogLevel {
34+
type Err = String;
35+
36+
fn from_str(s: &str) -> Result<Self, Self::Err> {
37+
match s.to_lowercase().as_str() {
38+
"trace" => Ok(LogLevel::Trace),
39+
"debug" => Ok(LogLevel::Debug),
40+
"info" => Ok(LogLevel::Info),
41+
"warn" => Ok(LogLevel::Warn),
42+
"error" => Ok(LogLevel::Error),
43+
_ => Err(format!("Invalid log level: {}", s)),
44+
}
45+
}
46+
}
47+
3148
impl Default for LogLevel {
3249
#[cfg(debug_assertions)]
3350
fn default() -> Self {
@@ -62,6 +79,29 @@ pub enum LogFormat {
6279
Json,
6380
}
6481

82+
impl LogFormat {
83+
pub fn as_str(&self) -> &'static str {
84+
match self {
85+
LogFormat::PrettyTree => "pretty-tree",
86+
LogFormat::PrettyCompact => "pretty-compact",
87+
LogFormat::Json => "json",
88+
}
89+
}
90+
}
91+
92+
impl FromStr for LogFormat {
93+
type Err = String;
94+
95+
fn from_str(s: &str) -> Result<Self, Self::Err> {
96+
match s.to_lowercase().as_str() {
97+
"pretty-tree" => Ok(LogFormat::PrettyTree),
98+
"pretty-compact" => Ok(LogFormat::PrettyCompact),
99+
"json" => Ok(LogFormat::Json),
100+
_ => Err(format!("Invalid log format: {}", s)),
101+
}
102+
}
103+
}
104+
65105
impl Default for LogFormat {
66106
#[cfg(debug_assertions)]
67107
fn default() -> Self {

0 commit comments

Comments
 (0)