use super::{Context, InfrastructureConfig};
use crate::error::{Mecha10Error, Result};
use anyhow::Context as _;
impl Context {
pub(super) fn load_infrastructure_config() -> Result<InfrastructureConfig> {
use crate::config::load_config;
use std::env;
use std::path::PathBuf;
if let Ok(redis_url) = Self::try_load_redis_from_mecha10_json() {
return Ok(InfrastructureConfig {
redis: crate::context::infra_config::RedisConfig {
url: redis_url,
max_connections: 10,
connection_timeout_ms: 5000,
},
control_plane_redis: None,
bridge: None,
postgres: None,
mlflow: None,
minio: None,
});
}
if let Ok(redis_url) = env::var("MECHA10_REDIS_URL").or_else(|_| env::var("REDIS_URL")) {
return Ok(InfrastructureConfig {
redis: crate::context::infra_config::RedisConfig {
url: redis_url,
max_connections: 10,
connection_timeout_ms: 5000,
},
control_plane_redis: None,
bridge: None,
postgres: None,
mlflow: None,
minio: None,
});
}
let environment = env::var("MECHA10_ENVIRONMENT").unwrap_or_else(|_| "development".to_string());
let config_filename = format!("infrastructure.{}.yaml", environment);
let paths_to_try = vec![
PathBuf::from(format!("config/{}", config_filename)),
PathBuf::from(format!("../../config/{}", config_filename)),
PathBuf::from(format!("../../../../config/{}", config_filename)),
];
for path in &paths_to_try {
if path.exists() {
return load_config(path)
.with_context(|| {
format!(
"Failed to load infrastructure config for environment '{}' from '{}'",
environment,
path.display()
)
})
.map_err(|e| Mecha10Error::Configuration(format!("{:#}", e)));
}
}
Err(Mecha10Error::Configuration(format!(
"Failed to load infrastructure config for environment '{}'. \
Tried paths: {}. \
Make sure the file exists, set MECHA10_REDIS_URL environment variable, \
or configure redis.url in mecha10.json.",
environment,
paths_to_try
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(", ")
)))
}
fn try_load_redis_from_mecha10_json() -> Result<String> {
use std::env;
let current_dir = env::current_dir()
.map_err(|e| Mecha10Error::Configuration(format!("Failed to get current directory: {}", e)))?;
let project_root = crate::fs_utils::find_project_root(¤t_dir).ok_or_else(|| {
Mecha10Error::Configuration("No mecha10.json found with redis.url configuration".to_string())
})?;
let path = project_root.join(crate::fs_utils::PROJECT_CONFIG_FILE);
let content = std::fs::read_to_string(&path)
.map_err(|e| Mecha10Error::Configuration(format!("Failed to read {}: {}", path.display(), e)))?;
let json: serde_json::Value = serde_json::from_str(&content)
.map_err(|e| Mecha10Error::Configuration(format!("Failed to parse {}: {}", path.display(), e)))?;
json.get("redis")
.and_then(|r| r.get("url"))
.and_then(|u| u.as_str())
.map(|s| s.to_string())
.ok_or_else(|| {
Mecha10Error::Configuration("No mecha10.json found with redis.url configuration".to_string())
})
}
pub(super) fn get_redis_url_from_config(config: &InfrastructureConfig) -> Result<String> {
if let Ok(url) = std::env::var("MECHA10_REDIS_URL") {
return Ok(url);
}
if let Ok(url) = std::env::var("REDIS_URL") {
return Ok(url);
}
Ok(config.redis.url.clone())
}
pub fn get_redis_url() -> Result<String> {
let config = Self::load_infrastructure_config()?;
Self::get_redis_url_from_config(&config)
}
pub fn get_control_plane_redis_url() -> Result<Option<String>> {
let config = Self::load_infrastructure_config()?;
Ok(config.control_plane_redis.map(|c| c.url))
}
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
use std::fs;
#[test]
#[serial]
fn try_load_redis_from_mecha10_json_finds_config_several_levels_up() {
let original_dir = std::env::current_dir().unwrap();
let project = tempfile::tempdir().unwrap();
fs::write(
project.path().join("mecha10.json"),
r#"{"redis": {"url": "redis://nested-project:6379"}}"#,
)
.unwrap();
let nested = project.path().join("a").join("b").join("c");
fs::create_dir_all(&nested).unwrap();
std::env::set_current_dir(&nested).unwrap();
let result = Context::try_load_redis_from_mecha10_json();
std::env::set_current_dir(original_dir).unwrap();
assert_eq!(result.unwrap(), "redis://nested-project:6379");
}
#[test]
#[serial]
fn try_load_redis_from_mecha10_json_errs_when_not_found() {
let original_dir = std::env::current_dir().unwrap();
let dir = tempfile::tempdir().unwrap();
std::env::set_current_dir(dir.path()).unwrap();
let result = Context::try_load_redis_from_mecha10_json();
std::env::set_current_dir(original_dir).unwrap();
assert!(result.is_err());
}
}