From e89ce29085322d3186eebb59de250ed3f40ab859 Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Sat, 4 Nov 2023 04:11:07 +0100 Subject: [PATCH] feat: auto-config file detection at startup (#281) The TOML config file is now detected at startup time using a new default `./config.toml` value. It makes sure to check this file path and load it if found. Otherwise, the behavior continues as usual. So it's not mandatory to provide a flag or env variable explicitly if the default file path is wanted. Also, additional debug information and file validations are provided. --- src/bin/server.rs | 2 +- src/server.rs | 10 ++++++++-- src/settings/cli.rs | 13 ++++++++++--- src/settings/mod.rs | 46 ++++++++++++++++++++++++++++------------------ src/winservice.rs | 12 +++++------- 5 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/bin/server.rs b/src/bin/server.rs index 367ad0f..b1cc092 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -25,7 +25,7 @@ fn main() -> Result { if let Some(commands) = opts.general.commands { match commands { Commands::Install {} => { - return winservice::install_service(opts.general.config_file); + return winservice::install_service(&opts.general.config_file); } Commands::Uninstall {} => { return winservice::uninstall_service(); diff --git a/src/server.rs b/src/server.rs index 8d95876..a98e97c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -109,8 +109,14 @@ impl Server { server_info!("log level: {}", general.log_level); // Config file option - if let Some(config_file) = general.config_file { - server_info!("config file: {}", config_file.display()); + let config_file = general.config_file; + if config_file.is_file() { + server_info!("config file used: {}", config_file.display()); + } else { + tracing::debug!( + "config file path not found or not a regular file: {}", + config_file.display() + ); } // Determine TCP listener either file descriptor or TCP socket diff --git a/src/settings/cli.rs b/src/settings/cli.rs index a7d8d8a..7bc1df9 100644 --- a/src/settings/cli.rs +++ b/src/settings/cli.rs @@ -11,6 +11,7 @@ use std::path::PathBuf; #[cfg(feature = "directory-listing")] use crate::directory_listing::DirListFmt; +use crate::Result; /// General server configuration available in CLI and config file options. #[derive(Parser, Debug)] @@ -344,9 +345,15 @@ pub struct General { /// Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. The maximum value is 255 seconds. pub grace_period: u8, - #[arg(long, short = 'w', env = "SERVER_CONFIG_FILE")] + #[arg( + long, + short = 'w', + default_value = "./config.toml", + value_parser = value_parser_pathbuf, + env = "SERVER_CONFIG_FILE" + )] /// Server TOML configuration file path. - pub config_file: Option, + pub config_file: PathBuf, #[arg( long, @@ -466,7 +473,7 @@ pub enum Commands { Uninstall {}, } -fn value_parser_pathbuf(s: &str) -> crate::Result { +fn value_parser_pathbuf(s: &str) -> Result { Ok(PathBuf::from(s)) } diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 2e43f08..e7f00f5 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -11,7 +11,7 @@ use globset::{Glob, GlobMatcher}; use headers::HeaderMap; use hyper::StatusCode; use regex::Regex; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use crate::{helpers, logger, Context, Result}; @@ -161,11 +161,14 @@ impl Settings { // Handle "config file options" and set them when available // NOTE: All config file based options shouldn't be mandatory, therefore `Some()` wrapped - if let Some((settings, path_resolved)) = get_file_settings(opts.config_file)? { - config_file = Some(path_resolved); + if let Some((settings, config_file_resolved)) = read_file_settings(&opts.config_file)? { + config_file = config_file_resolved; // File-based "general" options - if let Some(general) = settings.general { + let has_general_settings = settings.general.is_some(); + if has_general_settings { + let general = settings.general.unwrap(); + if let Some(v) = general.host { host = v } @@ -310,11 +313,20 @@ impl Settings { } } - // Logging system initialization + // Logging system initialization in config file context if log_init { logger::init(log_level.as_str())?; } - tracing::debug!("toml configuration file read successfully"); + + tracing::debug!("config file read successfully"); + tracing::debug!("config file path provided: {}", opts.config_file.display()); + tracing::debug!("config file path resolved: {}", config_file.display()); + + if !has_general_settings { + server_warn!( + "config file empty or no `general` settings found, using default values" + ); + } // File-based "advanced" options if let Some(advanced) = settings.advanced { @@ -473,7 +485,7 @@ impl Settings { }); } } else if log_init { - // Logging system initialization + // Logging system initialization on demand logger::init(log_level.as_str())?; } @@ -543,19 +555,17 @@ impl Settings { } } -fn get_file_settings(file_path_opt: Option) -> Result> { - if let Some(ref file_path) = file_path_opt { - if file_path.is_file() { - let file_path_resolved = file_path - .canonicalize() - .with_context(|| "error resolving toml config file path")?; +fn read_file_settings(config_file: &Path) -> Result> { + if config_file.is_file() { + let file_path_resolved = config_file + .canonicalize() + .with_context(|| "unable to resolve toml config file path")?; - let settings = FileSettings::read(&file_path_resolved).with_context(|| { - "can not read toml config file because has invalid or unsupported format/options" - })?; + let settings = FileSettings::read(&file_path_resolved).with_context(|| { + "unable to read toml config file because has invalid format or unsupported options" + })?; - return Ok(Some((settings, file_path_resolved))); - } + return Ok(Some((settings, file_path_resolved))); } Ok(None) } diff --git a/src/winservice.rs b/src/winservice.rs index b6bc5e7..64dab9d 100644 --- a/src/winservice.rs +++ b/src/winservice.rs @@ -9,7 +9,7 @@ use std::ffi::OsString; use std::thread; use std::time::Duration; -use std::{env, path::PathBuf}; +use std::{env, path::Path}; use windows_service::{ define_windows_service, @@ -174,7 +174,7 @@ pub fn run_server_as_service() -> Result { } /// Install a Windows Service for SWS. -pub fn install_service(config_file: Option) -> Result { +pub fn install_service(config_file: &Path) -> Result { let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE; let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?; @@ -185,11 +185,9 @@ pub fn install_service(config_file: Option) -> Result { let mut service_binary_arguments = vec![OsString::from("--windows-service=true")]; // Append a `--config-file` path to the binary arguments if present - if let Some(f) = config_file { - let f = helpers::adjust_canonicalization(&f); - if !f.is_empty() { - service_binary_arguments.push(OsString::from(["--config-file=", &f].concat())); - } + let f = helpers::adjust_canonicalization(config_file); + if !f.is_empty() { + service_binary_arguments.push(OsString::from(["--config-file=", &f].concat())); } // Run the current service as `System` type -- libgit2 1.7.2