1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Infrastructure configuration loading (Redis URLs, control plane, mecha10.json).
use super::{Context, InfrastructureConfig};
use crate::error::{Mecha10Error, Result};
use anyhow::Context as _;
impl Context {
/// Load infrastructure configuration based on environment
///
/// Priority order:
/// 1. mecha10.json (project configuration) - redis.url field
/// 2. Environment variables (MECHA10_REDIS_URL or REDIS_URL)
/// 3. config/infrastructure.<environment>.yaml
///
/// Set via MECHA10_ENVIRONMENT environment variable.
pub(super) fn load_infrastructure_config() -> Result<InfrastructureConfig> {
use crate::config::load_config;
use std::env;
use std::path::PathBuf;
// 1. Try loading from mecha10.json FIRST (project configuration)
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,
});
}
// 2. Check for environment variable overrides
// If REDIS_URL or MECHA10_REDIS_URL is set, use that and create minimal config
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,
});
}
// 3. Load from infrastructure.yaml
let environment = env::var("MECHA10_ENVIRONMENT").unwrap_or_else(|_| "development".to_string());
let config_filename = format!("infrastructure.{}.yaml", environment);
// Try multiple paths in order:
// 1. Current directory (for running from workspace root)
// 2. Workspace root (for running tests from target/debug/deps)
// 3. Two levels up (for running tests from packages/core/target/debug/deps)
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)));
}
}
// If no config file found, return error with helpful message
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(", ")
)))
}
/// Try to load Redis URL from mecha10.json
///
/// Looks for mecha10.json in current directory and reads redis.url field.
fn try_load_redis_from_mecha10_json() -> Result<String> {
use std::path::PathBuf;
// Try multiple paths to find mecha10.json
let paths_to_try = vec![
PathBuf::from("mecha10.json"),
PathBuf::from("../mecha10.json"),
PathBuf::from("../../mecha10.json"),
];
for path in &paths_to_try {
if !path.exists() {
continue;
}
// Read and parse mecha10.json
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)))?;
// Extract redis.url
if let Some(redis_url) = json.get("redis").and_then(|r| r.get("url")).and_then(|u| u.as_str()) {
return Ok(redis_url.to_string());
}
}
Err(Mecha10Error::Configuration(
"No mecha10.json found with redis.url configuration".to_string(),
))
}
/// Get Redis URL from infrastructure configuration (helper for internal use)
pub(super) fn get_redis_url_from_config(config: &InfrastructureConfig) -> Result<String> {
// First try environment variables (for backward compatibility and testing)
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);
}
// Use config
Ok(config.redis.url.clone())
}
/// Get Redis URL from infrastructure configuration
pub fn get_redis_url() -> Result<String> {
let config = Self::load_infrastructure_config()?;
Self::get_redis_url_from_config(&config)
}
/// Get control plane Redis URL from infrastructure configuration
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))
}
}