From 9fff5e4fecc6ac49012964ab761c6fc3ed7c31f4 Mon Sep 17 00:00:00 2001 From: fanyang Date: Tue, 16 Sep 2025 22:58:07 +0800 Subject: [PATCH] Add config validation flag (#1376) Add `--check-config` CLI option to validate configuration without starting network --- easytier/locales/app.yml | 3 +++ easytier/src/easytier-core.rs | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/easytier/locales/app.yml b/easytier/locales/app.yml index 9a4c60c..2dfa8f5 100644 --- a/easytier/locales/app.yml +++ b/easytier/locales/app.yml @@ -214,6 +214,9 @@ core_clap: stun_servers_v6: en: "Override default STUN servers, IPv6; If configured but empty, IPv6 STUN servers are not used" zh-CN: "覆盖内置的默认 IPv6 STUN server 列表;如果设置了但是为空,则不使用 IPv6 STUN servers;如果没设置,则使用默认 IPv6 STUN server 列表" + check_config: + en: Check config validity without starting the network + zh-CN: 检查配置文件的有效性并退出 core_app: panic_backtrace_save: diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index d6c19ed..2c8fecb 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -132,6 +132,9 @@ struct Cli { #[clap(long, help = t!("core_clap.generate_completions").to_string())] gen_autocomplete: Option, + + #[clap(long, help = t!("core_clap.check_config").to_string())] + check_config: bool, } #[derive(Parser, Debug)] @@ -1287,6 +1290,17 @@ async fn main() -> ExitCode { easytier::print_completions(shell, &mut cmd, "easytier-core"); return ExitCode::SUCCESS; } + + // Verify configurations + if cli.check_config { + if let Err(e) = validate_config(&cli).await { + eprintln!("Config validation failed: {:?}", e); + return ExitCode::FAILURE; + } else { + return ExitCode::SUCCESS; + } + } + let mut ret_code = 0; if let Err(e) = run_main(cli).await { @@ -1301,3 +1315,25 @@ async fn main() -> ExitCode { ExitCode::from(ret_code) } + +async fn validate_config(cli: &Cli) -> anyhow::Result<()> { + // Check if config file is provided + let config_files = cli + .config_file + .as_ref() + .ok_or_else(|| anyhow::anyhow!("--config-file is required when using --check-config"))?; + + for config_file in config_files { + if config_file == &PathBuf::from("-") { + let mut stdin = String::new(); + _ = tokio::io::stdin().read_to_string(&mut stdin).await?; + TomlConfigLoader::new_from_str(stdin.as_str()) + .with_context(|| "config source: stdin")?; + } else { + TomlConfigLoader::new(config_file) + .with_context(|| format!("config source: {:?}", config_file))?; + }; + } + + Ok(()) +}