This is an automated email from the ASF dual-hosted git repository.
piotr pushed a commit to branch connectors_test
in repository https://gitbox.apache.org/repos/asf/iggy.git
The following commit(s) were added to refs/heads/connectors_test by this push:
new 93e40465 Refactor common config provider, update modules config loader
93e40465 is described below
commit 93e404655005225e4347cf4ecd0eba239ea82143
Author: spetz <[email protected]>
AuthorDate: Mon Sep 22 16:29:08 2025 +0200
Refactor common config provider, update modules config loader
---
core/ai/mcp/src/configs.rs | 7 +-
core/ai/mcp/src/error.rs | 2 -
core/ai/mcp/src/main.rs | 21 +--
core/common/src/configs/mod.rs | 250 ++++++++++++++++++++++++---------
core/connectors/runtime/src/configs.rs | 7 +-
core/connectors/runtime/src/error.rs | 2 -
core/connectors/runtime/src/main.rs | 18 +--
core/server/src/configs/server.rs | 7 +-
8 files changed, 205 insertions(+), 109 deletions(-)
diff --git a/core/ai/mcp/src/configs.rs b/core/ai/mcp/src/configs.rs
index 1f2e6672..b1666309 100644
--- a/core/ai/mcp/src/configs.rs
+++ b/core/ai/mcp/src/configs.rs
@@ -197,7 +197,12 @@ impl std::fmt::Display for McpServerConfig {
impl McpServerConfig {
pub fn config_provider(path: String) ->
FileConfigProvider<McpServerEnvProvider> {
let default_config =
Toml::string(include_str!("../../../ai/mcp/config.toml"));
- FileConfigProvider::new(path, default_config,
McpServerEnvProvider::default())
+ FileConfigProvider::new(
+ path,
+ McpServerEnvProvider::default(),
+ true,
+ Some(default_config),
+ )
}
}
diff --git a/core/ai/mcp/src/error.rs b/core/ai/mcp/src/error.rs
index 0a8c0363..0344cfac 100644
--- a/core/ai/mcp/src/error.rs
+++ b/core/ai/mcp/src/error.rs
@@ -36,6 +36,4 @@ pub enum McpRuntimeError {
FailedToCreateConsumerId,
#[error("Invalid API path")]
InvalidApiPath,
- #[error("Configuration not found: {0}")]
- ConfigurationNotFound(String),
}
diff --git a/core/ai/mcp/src/main.rs b/core/ai/mcp/src/main.rs
index 1633fe1d..0b5792ed 100644
--- a/core/ai/mcp/src/main.rs
+++ b/core/ai/mcp/src/main.rs
@@ -34,6 +34,8 @@ mod error;
mod service;
mod stream;
+const DEFAULT_CONFIG_PATH: &str = "core/ai/mcp/config.toml";
+
#[tokio::main]
async fn main() -> Result<(), McpRuntimeError> {
let standard_font = FIGfont::standard().unwrap();
@@ -51,24 +53,9 @@ async fn main() -> Result<(), McpRuntimeError> {
);
}
- let config_path = env::var("IGGY_MCP_CONFIG_PATH");
- if let Ok(ref path) = config_path {
- let config_with_extension = if path.contains('.') {
- path.to_owned()
- } else {
- format!("{path}.toml")
- };
- eprintln!("Checking if config path exists:
{config_with_extension}...");
- if !std::fs::exists(&config_with_extension).unwrap_or_default() {
- return Err(McpRuntimeError::ConfigurationNotFound(
- config_with_extension,
- ));
- }
- }
-
- let config_path = config_path.unwrap_or_else(|_| "config".to_string());
+ let config_path =
+ env::var("IGGY_MCP_CONFIG_PATH").unwrap_or_else(|_|
DEFAULT_CONFIG_PATH.to_string());
eprintln!("Configuration file path: {config_path}");
-
let config: McpServerConfig = McpServerConfig::config_provider(config_path)
.load_config()
.await
diff --git a/core/common/src/configs/mod.rs b/core/common/src/configs/mod.rs
index 7a10d853..cf9159ba 100644
--- a/core/common/src/configs/mod.rs
+++ b/core/common/src/configs/mod.rs
@@ -16,6 +16,15 @@
* under the License.
*/
+//! Configuration module providing flexible configuration loading with file
and environment support.
+//!
+//! This module provides a trait-based configuration system that supports:
+//! - Loading configuration from TOML files
+//! - Environment variable overrides with complex path resolution
+//! - Array and HashMap field overrides
+//! - JSON value field handling
+//! - Automatic type conversion and validation
+
use figment::{
Figment, Profile, Provider,
providers::{Data, Format, Toml},
@@ -23,94 +32,166 @@ use figment::{
};
use serde::{Serialize, de::DeserializeOwned};
use std::{env, fmt::Display, future::Future, marker::PhantomData, path::Path};
-use toml::{Value as TomlValue, map::Map};
+use toml::{Value as TomlValue, map::Map as TomlMap};
+
+// Constants for configuration handling
+const SECRET_MASK: &str = "******";
+const ARRAY_SEPARATOR: char = '_';
+const PATH_SEPARATOR: &str = ".";
+
+/// Type alias for configuration profiles mapping
+type ProfileMap = FigmentMap<Profile, Dict>;
+
+/// Type alias for configuration that can be serialized, deserialized, has
defaults, and can be displayed
+pub trait ConfigurationType: Serialize + DeserializeOwned + Default + Display
{}
+impl<T: Serialize + DeserializeOwned + Default + Display> ConfigurationType
for T {}
+/// Errors that can occur during configuration loading and processing
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigurationError {
+ /// Failed to load configuration from file or environment
CannotLoadConfiguration,
+ /// Failed to serialize default configuration to TOML
+ DefaultSerializationFailed,
+ /// Failed to parse default configuration TOML
+ DefaultParsingFailed,
+ /// Environment variable parsing failed
+ EnvironmentVariableParsingFailed,
+}
+
+impl Display for ConfigurationError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::CannotLoadConfiguration => write!(f, "Cannot load
configuration"),
+ Self::DefaultSerializationFailed => {
+ write!(f, "Failed to serialize default configuration")
+ }
+ Self::DefaultParsingFailed => write!(f, "Failed to parse default
configuration"),
+ Self::EnvironmentVariableParsingFailed => {
+ write!(f, "Failed to parse environment variables")
+ }
+ }
+ }
}
-pub trait ConfigProvider<T: Serialize + DeserializeOwned + Default + Display> {
+impl std::error::Error for ConfigurationError {}
+
+/// Trait for configuration providers that can load configuration of a
specific type
+pub trait ConfigProvider<T: ConfigurationType> {
+ /// Load configuration asynchronously
fn load_config(self) -> impl Future<Output = Result<T,
ConfigurationError>>;
}
-pub struct FileConfigProvider<T: Provider> {
- path: String,
- default_config: Data<Toml>,
- env_provider: T,
+/// File-based configuration provider that combines file, default, and
environment configurations
+pub struct FileConfigProvider<P> {
+ file_path: String,
+ default_config: Option<Data<Toml>>,
+ env_provider: P,
+ display_config: bool,
}
-impl<T: Provider> FileConfigProvider<T> {
- pub fn new(path: String, default_config: Data<Toml>, env_provider: T) ->
Self {
+impl<P: Provider> FileConfigProvider<P> {
+ /// Create a new file configuration provider
+ ///
+ /// # Arguments
+ /// * `file_path` - Path to the configuration file
+ /// * `env_provider` - Environment variable provider
+ /// * `default_config` - Optional default configuration data
+ /// * `display_config` - Whether to display the loaded configuration
+ pub fn new(
+ file_path: String,
+ env_provider: P,
+ display_config: bool,
+ default_config: Option<Data<Toml>>,
+ ) -> Self {
Self {
- path,
- default_config,
+ file_path,
env_provider,
+ default_config,
+ display_config,
}
}
}
+/// Custom environment variable provider with advanced override capabilities
+///
+/// This provider supports:
+/// - Prefix-based filtering of environment variables
+/// - Secret key masking for security
+/// - Complex path resolution for nested structures
+/// - Array index overrides
+/// - HashMap field overrides
+/// - JSON value field handling
#[derive(Debug, Clone)]
-pub struct CustomEnvProvider<T: Serialize + DeserializeOwned + Default +
Display> {
+pub struct CustomEnvProvider<T: ConfigurationType> {
prefix: String,
secret_keys: Vec<String>,
- _data: PhantomData<T>,
+ _phantom: PhantomData<T>,
}
-impl<T: Serialize + DeserializeOwned + Default + Display> CustomEnvProvider<T>
{
+impl<T: ConfigurationType> CustomEnvProvider<T> {
+ /// Create a new custom environment provider
+ ///
+ /// # Arguments
+ /// * `prefix` - Environment variable prefix to filter by
+ /// * `secret_keys` - Keys that should be masked in logs
pub fn new(prefix: &str, secret_keys: &[&str]) -> Self {
Self {
prefix: prefix.to_string(),
secret_keys: secret_keys.iter().map(|s| s.to_string()).collect(),
- _data: PhantomData,
+ _phantom: PhantomData,
}
}
- pub fn deserialize(&self) -> Result<FigmentMap<Profile, Dict>,
ConfigurationError> {
+ /// Deserialize environment variables into a configuration profile map
+ pub fn deserialize(&self) -> Result<ProfileMap, ConfigurationError> {
let default_config = toml::to_string(&T::default())
- .expect("Cannot serialize default Config. Something's terribly
wrong.");
+ .map_err(|_| ConfigurationError::DefaultSerializationFailed)?;
+
let toml_value: TomlValue = toml::from_str(&default_config)
- .expect("Cannot parse default Config. Something's terribly
wrong.");
+ .map_err(|_| ConfigurationError::DefaultParsingFailed)?;
+
let mut source_dict = Dict::new();
if let TomlValue::Table(table) = toml_value {
Self::walk_toml_table_to_dict("", table, &mut source_dict);
}
- let mut new_dict = Dict::new();
+ let mut target_dict = Dict::new();
for (key, mut value) in env::vars() {
let env_key = key.to_uppercase();
- if !env_key.starts_with(self.prefix.as_str()) {
+ if !env_key.starts_with(&self.prefix) {
continue;
}
+
let keys: Vec<String> = env_key[self.prefix.len()..]
- .split('_')
+ .split(ARRAY_SEPARATOR)
.map(|k| k.to_lowercase())
.collect();
- let env_var_value = Self::try_parse_value(&value);
+
+ let env_var_value = Self::parse_environment_value(&value);
+
if self.secret_keys.contains(&env_key) {
- value = "******".to_string();
+ value = SECRET_MASK.to_string();
}
println!("{env_key} value changed to: {value} from environment
variable");
- Self::insert_overridden_values_from_env(
- &source_dict,
- &mut new_dict,
- keys.clone(),
- env_var_value.clone(),
- );
+ Self::insert_environment_override(&source_dict, &mut target_dict,
keys, env_var_value);
}
- let mut data = FigmentMap::new();
- data.insert(Profile::default(), new_dict);
+
+ let mut data = ProfileMap::new();
+ data.insert(Profile::default(), target_dict);
Ok(data)
}
- fn walk_toml_table_to_dict(prefix: &str, table: Map<String, TomlValue>,
dict: &mut Dict) {
+ /// Walk through TOML table and convert to dictionary structure
+ fn walk_toml_table_to_dict(prefix: &str, table: TomlMap<String,
TomlValue>, dict: &mut Dict) {
for (key, value) in table {
let new_prefix = if prefix.is_empty() {
key.clone()
} else {
- format!("{prefix}.{key}")
+ format!("{prefix}{PATH_SEPARATOR}{key}")
};
+
match value {
TomlValue::Table(inner_table) => {
let mut nested_dict = Dict::new();
@@ -124,7 +205,8 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
}
}
- fn insert_overridden_values_from_env(
+ /// Insert environment variable override into target dictionary
+ fn insert_environment_override(
source: &Dict,
target: &mut Dict,
keys: Vec<String>,
@@ -138,16 +220,18 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
if let Some((idx_pos, array_index)) = Self::find_array_index(&keys) {
Self::handle_array_override(source, target, &keys, idx_pos,
array_index, value);
} else {
- Self::handle_dict_override(source, target, &keys, value);
+ Self::handle_dictionary_override(source, target, &keys, value);
}
}
+ /// Find array index in key path
fn find_array_index(keys: &[String]) -> Option<(usize, usize)> {
keys.iter()
.enumerate()
.find_map(|(i, key)| key.parse::<usize>().ok().map(|idx| (i, idx)))
}
+ /// Handle array field override with complex path resolution
fn handle_array_override(
source: &Dict,
target: &mut Dict,
@@ -160,7 +244,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
let remaining_path = &keys[idx_pos + 1..];
// Navigate to array container
- let current_target = match Self::navigate_to_dict(
+ let current_target = match Self::navigate_to_dictionary(
target,
&path_to_array[..path_to_array.len().saturating_sub(1)],
) {
@@ -196,7 +280,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
if remaining_path.is_empty() {
arr[array_index] = value;
} else if let FigmentValue::Dict(_, elem_dict) = &mut
arr[array_index] {
- Self::insert_overridden_values_from_env(
+ Self::insert_environment_override(
&Dict::new(),
elem_dict,
remaining_path.to_vec(),
@@ -206,7 +290,8 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
}
}
- fn handle_dict_override(
+ /// Handle dictionary field override with HashMap detection
+ fn handle_dictionary_override(
source: &Dict,
target: &mut Dict,
keys: &[String],
@@ -262,6 +347,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
}
}
+ /// Try to handle HashMap override patterns
fn try_handle_hashmap_override(
source: &Dict,
target: &mut Dict,
@@ -277,11 +363,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
// Check if this is actually a HashMap field in the source
let source_hashmap = match source.get(potential_hashmap_key) {
Some(FigmentValue::Dict(_, hashmap_dict)) => {
- // For it to be a HashMap, it should either:
- // 1. Have Dict values (actual HashMap entries)
- // 2. Be empty (empty HashMap)
- // 3. But NOT have regular struct fields
-
+ // Determine if this is a HashMap or regular struct
let has_dict_values = hashmap_dict
.values()
.any(|v| matches!(v, FigmentValue::Dict(_, _)));
@@ -289,7 +371,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
.values()
.any(|v| !matches!(v, FigmentValue::Dict(_, _)));
- // If it has non-Dict values mixed with Dict values, it's
probably a regular struct, not a HashMap
+ // If it has non-Dict values mixed with Dict values, it's
probably a regular struct
if has_non_dict_values && has_dict_values {
return Some(false);
}
@@ -299,7 +381,6 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
return Some(false);
}
- // If it's empty or only has Dict values, it could be a HashMap
hashmap_dict
}
_ => return Some(false),
@@ -307,7 +388,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
// Try to find the best HashMap entry key
if let Some((entry_key, remaining_keys)) =
- Self::find_best_hashmap_split(source_hashmap, &keys[1..])
+ Self::find_optimal_hashmap_split(source_hashmap, &keys[1..])
{
return Some(Self::apply_hashmap_override(
target,
@@ -322,7 +403,8 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
Some(false)
}
- fn find_best_hashmap_split(
+ /// Find optimal HashMap key split for environment variable path
+ fn find_optimal_hashmap_split(
source_hashmap: &Dict,
keys: &[String],
) -> Option<(String, Vec<String>)> {
@@ -359,6 +441,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
Some((simple_entry_key, simple_remaining))
}
+ /// Check if a field path is valid within a dictionary structure
fn is_valid_field_path(dict: &Dict, keys: &[String]) -> bool {
if keys.is_empty() {
return true;
@@ -400,6 +483,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
true
}
+ /// Apply HashMap override to target configuration
fn apply_hashmap_override(
target: &mut Dict,
hashmap_key: &str,
@@ -430,13 +514,13 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
} else if let Some(FigmentValue::Dict(_, entry_dict)) =
target_hashmap.get_mut(entry_key)
{
- // Check if we need special handling for serde_json::Value
fields
+ // Check if we need special handling for JSON value fields
if let Some((json_field, json_keys)) =
Self::find_json_value_field_split(entry_dict,
remaining_keys)
{
Self::handle_json_value_override(entry_dict, &json_field,
json_keys, value);
} else {
- Self::insert_overridden_values_from_env(
+ Self::insert_environment_override(
&Dict::new(),
entry_dict,
remaining_keys.to_vec(),
@@ -450,6 +534,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
false
}
+ /// Find JSON value field split in remaining keys
fn find_json_value_field_split(dict: &Dict, keys: &[String]) ->
Option<(String, Vec<String>)> {
if keys.is_empty() {
return None;
@@ -475,6 +560,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
None
}
+ /// Handle JSON value field override
fn handle_json_value_override(
entry_dict: &mut Dict,
json_field: &str,
@@ -504,6 +590,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
}
}
+ /// Set nested JSON field value
fn set_nested_json_field(dict: &mut Dict, keys: &[String], value:
FigmentValue) {
if keys.is_empty() {
return;
@@ -514,7 +601,8 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
dict.insert(full_field_name, value);
}
- fn navigate_to_dict<'a>(target: &'a mut Dict, path: &[String]) ->
Option<&'a mut Dict> {
+ /// Navigate to a specific dictionary in the nested structure
+ fn navigate_to_dictionary<'a>(target: &'a mut Dict, path: &[String]) ->
Option<&'a mut Dict> {
if path.is_empty() {
return Some(target);
}
@@ -542,6 +630,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
Some(current)
}
+ /// Find source array value for copying to target
fn find_source_array(source: &Dict, path: &[String], array_key: &str) ->
Option<FigmentValue> {
if path.is_empty() {
return None;
@@ -566,6 +655,7 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
current.get(array_key).cloned()
}
+ /// Convert TOML value to Figment value
fn toml_to_figment_value(toml_value: &TomlValue) -> FigmentValue {
match toml_value {
TomlValue::String(s) => FigmentValue::from(s.clone()),
@@ -577,39 +667,54 @@ impl<T: Serialize + DeserializeOwned + Default + Display>
CustomEnvProvider<T> {
FigmentValue::from(vec)
}
TomlValue::Table(tbl) => {
- let mut dict = figment::value::Dict::new();
+ let mut dict = Dict::new();
for (key, value) in tbl.iter() {
dict.insert(key.clone(),
Self::toml_to_figment_value(value));
}
FigmentValue::from(dict)
}
- TomlValue::Datetime(_) => todo!("not implemented yet!"),
+ TomlValue::Datetime(_) => {
+ // For now, convert datetime to string representation
+ FigmentValue::from(toml_value.to_string())
+ }
}
}
- fn try_parse_value(value: &str) -> FigmentValue {
+ /// Parse environment variable value with type inference
+ fn parse_environment_value(value: &str) -> FigmentValue {
+ // Handle array syntax
if value.starts_with('[') && value.ends_with(']') {
- let value = value.trim_start_matches('[').trim_end_matches(']');
- let values: Vec<FigmentValue> =
value.split(',').map(Self::try_parse_value).collect();
+ let inner_value =
value.trim_start_matches('[').trim_end_matches(']');
+ let values: Vec<FigmentValue> = inner_value
+ .split(',')
+ .map(|s| Self::parse_environment_value(s.trim()))
+ .collect();
return FigmentValue::from(values);
}
- if value == "true" {
- return FigmentValue::from(true);
- }
- if value == "false" {
- return FigmentValue::from(false);
+
+ // Handle boolean values
+ match value.to_lowercase().as_str() {
+ "true" => return FigmentValue::from(true),
+ "false" => return FigmentValue::from(false),
+ _ => {}
}
+
+ // Try parsing as integer
if let Ok(int_val) = value.parse::<i64>() {
return FigmentValue::from(int_val);
}
+
+ // Try parsing as float
if let Ok(float_val) = value.parse::<f64>() {
return FigmentValue::from(float_val);
}
+
+ // Default to string
FigmentValue::from(value)
}
}
-/// This does exactly the same as Figment does internally.
+/// Check if a file exists using the same logic as Figment
fn file_exists<P: AsRef<Path>>(path: P) -> bool {
let path = path.as_ref();
@@ -636,23 +741,26 @@ fn file_exists<P: AsRef<Path>>(path: P) -> bool {
}
}
-impl<T: Serialize + DeserializeOwned + Default + Display, P: Provider + Clone>
ConfigProvider<T>
- for FileConfigProvider<P>
-{
+impl<T: ConfigurationType, P: Provider + Clone> ConfigProvider<T> for
FileConfigProvider<P> {
async fn load_config(self) -> Result<T, ConfigurationError> {
- println!("Loading config from path: '{}'...", self.path);
+ println!("Loading config from path: '{}'...", self.file_path);
// Start with the default configuration
- let mut config_builder = Figment::new().merge(self.default_config);
+ let mut config_builder = Figment::new();
+ if let Some(default) = self.default_config {
+ config_builder = config_builder.merge(default);
+ } else {
+ println!("No default configuration provided.");
+ }
// If the config file exists, merge it into the configuration
- if file_exists(&self.path) {
- println!("Found configuration file at path: '{}'.", self.path);
- config_builder = config_builder.merge(Toml::file(&self.path));
+ if file_exists(&self.file_path) {
+ println!("Found configuration file at path: '{}'.",
self.file_path);
+ config_builder = config_builder.merge(Toml::file(&self.file_path));
} else {
println!(
"Configuration file not found at path: '{}'. Using default
configuration from embedded config",
- self.path
+ self.file_path
);
}
@@ -665,7 +773,9 @@ impl<T: Serialize + DeserializeOwned + Default + Display,
P: Provider + Clone> C
match config_result {
Ok(config) => {
println!("Config loaded successfully.");
- println!("Using Config: {config}");
+ if self.display_config {
+ println!("Using Config: {config}");
+ }
Ok(config)
}
Err(e) => {
diff --git a/core/connectors/runtime/src/configs.rs
b/core/connectors/runtime/src/configs.rs
index d8778532..12504a69 100644
--- a/core/connectors/runtime/src/configs.rs
+++ b/core/connectors/runtime/src/configs.rs
@@ -164,7 +164,12 @@ impl Default for HttpApiConfig {
impl RuntimeConfig {
pub fn config_provider(path: String) ->
FileConfigProvider<ConnectorsEnvProvider> {
let default_config =
Toml::string(include_str!("../../../connectors/runtime/config.toml"));
- FileConfigProvider::new(path, default_config,
ConnectorsEnvProvider::default())
+ FileConfigProvider::new(
+ path,
+ ConnectorsEnvProvider::default(),
+ true,
+ Some(default_config),
+ )
}
}
diff --git a/core/connectors/runtime/src/error.rs
b/core/connectors/runtime/src/error.rs
index f8674df8..c622feab 100644
--- a/core/connectors/runtime/src/error.rs
+++ b/core/connectors/runtime/src/error.rs
@@ -22,8 +22,6 @@ use thiserror::Error;
pub enum RuntimeError {
#[error("Invalid configuration: {0}")]
InvalidConfiguration(String),
- #[error("Configuration not found: {0}")]
- ConfigurationNotFound(String),
#[error("Failed to serialize topic metadata")]
FailedToSerializeTopicMetadata,
#[error("Failed to serialize messages metadata")]
diff --git a/core/connectors/runtime/src/main.rs
b/core/connectors/runtime/src/main.rs
index 1eecc3cc..1baa40eb 100644
--- a/core/connectors/runtime/src/main.rs
+++ b/core/connectors/runtime/src/main.rs
@@ -54,8 +54,8 @@ mod transform;
static GLOBAL: MiMalloc = MiMalloc;
static PLUGIN_ID: AtomicU32 = AtomicU32::new(1);
-
const ALLOWED_PLUGIN_EXTENSIONS: [&str; 3] = ["so", "dylib", "dll"];
+const DEFAULT_CONFIG_PATH: &str = "core/connectors/runtime/config.toml";
#[derive(WrapperApi)]
struct SourceApi {
@@ -108,20 +108,8 @@ async fn main() -> Result<(), RuntimeError> {
.with(EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("INFO")))
.init();
- let config_path = env::var("IGGY_CONNECTORS_CONFIG_PATH");
- if let Ok(ref path) = config_path {
- let config_with_extension = if path.contains('.') {
- path.to_owned()
- } else {
- format!("{path}.toml")
- };
- info!("Checking if config path exists: {config_with_extension}...");
- if !std::fs::exists(&config_with_extension).unwrap_or_default() {
- return
Err(RuntimeError::ConfigurationNotFound(config_with_extension));
- }
- }
-
- let config_path = config_path.unwrap_or_else(|_| "config".to_string());
+ let config_path =
+ env::var("IGGY_CONNECTORS_CONFIG_PATH").unwrap_or_else(|_|
DEFAULT_CONFIG_PATH.to_string());
info!("Starting Iggy Connectors Runtime, loading configuration from:
{config_path}...");
let config: RuntimeConfig = RuntimeConfig::config_provider(config_path)
diff --git a/core/server/src/configs/server.rs
b/core/server/src/configs/server.rs
index c977075b..f0d347ea 100644
--- a/core/server/src/configs/server.rs
+++ b/core/server/src/configs/server.rs
@@ -236,7 +236,12 @@ impl ServerConfig {
pub fn config_provider(path: String) ->
FileConfigProvider<ServerEnvProvider> {
let default_config =
Toml::string(include_str!("../../../configs/server.toml"));
- FileConfigProvider::new(path, default_config,
ServerEnvProvider::default())
+ FileConfigProvider::new(
+ path,
+ ServerEnvProvider::default(),
+ true,
+ Some(default_config),
+ )
}
}