Skip to content

Commit fc2707c

Browse files
committed
feat: unify config reading behavior
1 parent 5262f2f commit fc2707c

14 files changed

Lines changed: 366 additions & 224 deletions

File tree

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ slog-stdlog = "4.1.0"
118118
num-bigint = "0.4.6"
119119
bigdecimal = "0.4.8"
120120
bitvec = "1.0.1"
121-
config = "0.13.4"
121+
config = "0.15.9"
122122
tempfile = "3.21.0"
123123
md-5 = "0.10.6"
124124
bitflags = "2.10.0"

curvine-cli/src/main.rs

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ use curvine_client::unified::UnifiedFileSystem;
2323
use curvine_common::conf::ClusterConf;
2424
use curvine_common::version;
2525
use orpc::common::{Logger, Utils};
26-
use orpc::io::net::InetAddr;
2726
use orpc::runtime::RpcRuntime;
28-
use orpc::{err_box, CommonResult};
27+
use orpc::CommonResult;
28+
use serde::Serialize;
2929
use std::sync::Arc;
3030

31-
#[derive(Parser, Debug)]
31+
#[derive(Parser, Debug, Serialize)]
3232
#[command(author, version, about, long_about = None)]
3333
pub struct CurvineArgs {
3434
/// Configuration file path (optional)
@@ -48,58 +48,20 @@ pub struct CurvineArgs {
4848
)]
4949
pub master_addrs: Option<String>,
5050

51+
#[serde(skip_serializing)]
5152
#[command(subcommand)]
5253
command: Commands,
5354
}
5455

5556
impl CurvineArgs {
56-
/// Get cluster configuration with priority: CLI args > config file > env vars > defaults
5757
pub fn get_conf(&self) -> CommonResult<ClusterConf> {
58-
// Priority 1: Try to load from config file (CLI arg or env var)
58+
// Try to load from config file (CLI arg or env var)
5959
let conf_path = self
6060
.conf
6161
.clone()
6262
.or_else(|| std::env::var(ClusterConf::ENV_CONF_FILE).ok());
6363

64-
let mut conf = if let Some(path) = conf_path {
65-
match ClusterConf::from(&path) {
66-
Ok(c) => c,
67-
Err(e) => {
68-
eprintln!("Warning: Failed to load config file '{}': {}", path, e);
69-
eprintln!("Using default configuration");
70-
Self::create_default_conf()
71-
}
72-
}
73-
} else {
74-
println!("No config file specified, using default configuration");
75-
Self::create_default_conf()
76-
};
77-
78-
// Priority 2: Override with CLI master_addrs if provided
79-
if let Some(master_addrs) = &self.master_addrs {
80-
let mut vec = vec![];
81-
for node in master_addrs.split(',') {
82-
let tmp: Vec<&str> = node.split(':').collect();
83-
if tmp.len() != 2 {
84-
return err_box!("Invalid master_addrs format: '{}'. Expected format: 'host1:port1,host2:port2'", master_addrs);
85-
}
86-
let hostname = tmp[0].to_string();
87-
let port: u16 = tmp[1]
88-
.parse()
89-
.map_err(|_| format!("Invalid port number in master_addrs: '{}'", tmp[1]))?;
90-
vec.push(InetAddr::new(hostname, port));
91-
}
92-
conf.client.master_addrs = vec;
93-
}
94-
95-
// Initialize configuration (parse string values to actual types)
96-
conf.client.init()?;
97-
98-
Ok(conf)
99-
}
100-
101-
fn create_default_conf() -> ClusterConf {
102-
ClusterConf::default()
64+
ClusterConf::from(conf_path.unwrap(), Some(&self), Some("cli"))
10365
}
10466
}
10567

curvine-common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ slog = { workspace = true }
4040
slog-stdlog = { workspace = true }
4141
chrono = { workspace = true }
4242
trait-variant = { workspace = true }
43+
config = { workspace = true }
4344

4445
anyhow = { workspace = true }
4546
tracing = { workspace = true }

curvine-common/src/conf/cluster_conf.rs

Lines changed: 102 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@ use crate::conf::CliConf;
1616
use crate::conf::{ClientConf, FuseConf, JobConf, JournalConf, MasterConf, WorkerConf};
1717
use crate::rocksdb::DBConf;
1818
use crate::version;
19+
use config::{Config, Environment, File, FileFormat};
1920
use log::info;
2021
use orpc::client::{ClientConf as RpcConf, ClientFactory, SyncClient};
2122
use orpc::common::{LogConf, Utils};
2223
use orpc::io::net::{InetAddr, NodeAddr};
2324
use orpc::io::retry::TimeBondedRetryBuilder;
2425
use orpc::server::ServerConf;
25-
use orpc::{err_box, try_err, CommonResult};
26+
use orpc::{err_box, CommonResult};
2627
use serde::{Deserialize, Serialize};
27-
use std::env;
28+
use serde_json::{json, Value};
2829
use std::fmt::{Display, Formatter};
29-
use std::fs::read_to_string;
3030
use std::time::Duration;
3131

3232
// Cluster configuration files.
@@ -75,37 +75,110 @@ impl ClusterConf {
7575
pub const ENV_CLIENT_HOSTNAME: &'static str = "CURVINE_CLIENT_HOSTNAME";
7676
pub const ENV_CONF_FILE: &'static str = "CURVINE_CONF_FILE";
7777

78-
pub fn from<T: AsRef<str>>(path: T) -> CommonResult<Self> {
79-
let str = try_err!(read_to_string(path.as_ref()));
80-
let mut conf = try_err!(toml::from_str::<Self>(&str));
81-
82-
if let Ok(v) = env::var(Self::ENV_MASTER_HOSTNAME) {
83-
conf.master.hostname = v.to_owned();
84-
conf.journal.hostname = v;
85-
}
78+
pub fn argstostr<U: Serialize>(args_name: &str, args: &U) -> String {
79+
let args_value: Value = serde_json::to_value(args).unwrap();
80+
81+
let result = if let Some(master_addrs_str) =
82+
args_value.get("master_addrs").and_then(|v| v.as_str())
83+
{
84+
let addrs: Vec<Value> = master_addrs_str
85+
.split(',')
86+
.filter(|s| !s.trim().is_empty())
87+
.map(|addr| {
88+
let addr = addr.trim();
89+
let parts: Vec<&str> = addr.split(':').collect();
90+
91+
if parts.len() == 2 {
92+
let hostname = parts[0];
93+
let port = parts[1]
94+
.parse::<u16>()
95+
.map_err(|_| format!("Invalid port: {}", parts[1]))?;
96+
97+
Ok(json!({
98+
"hostname": hostname,
99+
"port": port
100+
}))
101+
} else {
102+
Err(format!("Invalid address format: {}", addr))
103+
}
104+
})
105+
.collect::<Result<Vec<_>, _>>()
106+
.unwrap();
107+
108+
json!({
109+
args_name: args_value,
110+
"client": {
111+
"master_addrs": addrs
112+
}
113+
})
114+
} else {
115+
json!({
116+
args_name: args_value
117+
})
118+
};
86119

87-
// Apply worker hostname from environment variable (used by worker process)
88-
if let Ok(v) = env::var(Self::ENV_WORKER_HOSTNAME) {
89-
conf.worker.hostname = v;
90-
}
120+
serde_json::to_string_pretty(&result).unwrap()
121+
}
91122

92-
// Apply client hostname from environment variable
93-
if let Ok(v) = env::var(Self::ENV_CLIENT_HOSTNAME) {
94-
conf.client.hostname = v;
123+
pub fn from<T: AsRef<str>, U: Serialize>(
124+
path: T,
125+
args: Option<&U>,
126+
args_name: Option<&str>,
127+
) -> CommonResult<Self> {
128+
let cli_source = if let (Some(name), Some(args_value)) = (args_name, args) {
129+
ClusterConf::argstostr(name, args_value)
130+
} else {
131+
"{}".to_string()
132+
};
133+
134+
// Get cluster configuration with priority: CLI args > env vars > config file > defaults
135+
let config = Config::builder()
136+
.add_source(File::new(path.as_ref(), FileFormat::Toml))
137+
.add_source(Environment::with_prefix("CURVINE").separator("_"))
138+
.add_source(File::from_str(&cli_source, FileFormat::Json));
139+
140+
let mut conf;
141+
match config.build() {
142+
Ok(config) => {
143+
conf = Self {
144+
format_master: true,
145+
format_worker: true,
146+
testing: false,
147+
cluster_id: "curvine".to_string(),
148+
master: config.get::<MasterConf>("master").unwrap_or_default(),
149+
journal: config.get::<JournalConf>("journal").unwrap_or_default(),
150+
worker: config.get::<WorkerConf>("worker").unwrap_or_default(),
151+
log: config.get::<LogConf>("log").unwrap_or_default(),
152+
client: config.get::<ClientConf>("client").unwrap_or_default(),
153+
fuse: config.get::<FuseConf>("fuse").unwrap_or_default(),
154+
s3_gateway: config
155+
.get::<S3GatewayConf>("s3_gateway")
156+
.unwrap_or_default(),
157+
job: config.get::<JobConf>("job").unwrap_or_default(),
158+
cli: config.get::<CliConf>("cli").unwrap_or_default(),
159+
};
160+
}
161+
Err(e) => {
162+
eprintln!(
163+
"Warning: Failed to load config file '{}': {}",
164+
path.as_ref(),
165+
e
166+
);
167+
eprintln!("Using default configuration");
168+
conf = ClusterConf::default();
169+
}
95170
}
96171

97172
conf.master.init()?;
98173
conf.client.init()?;
99174
conf.fuse.init()?;
100175
conf.job.init()?;
101-
102176
if conf.client.master_addrs.is_empty() {
103177
for peer in &mut conf.journal.journal_addrs {
104178
let node = InetAddr::new(&peer.hostname, conf.master.rpc_port);
105179
conf.client.master_addrs.push(node);
106180
}
107181
}
108-
109182
Ok(conf)
110183
}
111184

@@ -268,7 +341,7 @@ impl ClusterConf {
268341

269342
impl Default for ClusterConf {
270343
fn default() -> Self {
271-
Self {
344+
let mut conf = Self {
272345
format_master: true,
273346
format_worker: true,
274347
testing: false,
@@ -282,7 +355,14 @@ impl Default for ClusterConf {
282355
s3_gateway: Default::default(),
283356
job: Default::default(),
284357
cli: Default::default(),
285-
}
358+
};
359+
360+
conf.master.init().unwrap();
361+
conf.client.init().unwrap();
362+
conf.fuse.init().unwrap();
363+
conf.job.init().unwrap();
364+
365+
conf
286366
}
287367
}
288368

curvine-common/src/conf/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ mod tests {
5555
#[test]
5656
fn cluster() {
5757
let path = "../etc/curvine-cluster.toml";
58-
let conf = ClusterConf::from(path).unwrap();
58+
let conf = ClusterConf::from(path, None::<&()>, None).unwrap();
5959
println!("conf = {:#?}", conf)
6060
}
6161

curvine-fuse/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ mini-moka = { workspace = true }
2727
fxhash = { workspace = true }
2828
dashmap = { workspace = true }
2929
bitflags = { workspace = true }
30+
serde = { workspace = true }
31+
serde_with = { workspace = true }
3032
axum = { workspace = true }
3133
tracing = { workspace = true }
3234
tracing-appender = { workspace = true }

0 commit comments

Comments
 (0)