Skip to content

Commit

Permalink
Merge pull request #8 from kasbuunk/validate-config
Browse files Browse the repository at this point in the history
feat: configure clock cycle interval
  • Loading branch information
kasbuunk authored Dec 14, 2024
2 parents 46fb16a + 16e542a commit 7083f87
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 25 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ env_logger = "0.11.1"
futures = "0.3.30"
futures-util = "0.3.30"
http-body-util = "0.1.0"
humantime = "2.1.0"
hyper = { version = "1.1.0", features = ["server", "http1"] }
hyper-util = { version = "0.1.3", features = ["full"] }
log = { version = "0.4.20" }
Expand Down
1 change: 1 addition & 0 deletions sample.ron
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
automigrate: true,
reset_state: false,
log_level: "debug",
clock_cycle_interval: "100ms",
metrics: Prometheus(
Config(
port: 9090,
Expand Down
81 changes: 75 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,107 @@
use core::time;

use serde::Deserialize;

use crate::grpc;
use crate::metrics;
use crate::nats;
use crate::postgres;

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone)]
pub struct Config {
pub automigrate: bool,
pub log_level: String,
pub log_level: log::Level,
pub clock_cycle_interval: time::Duration,
pub metrics: Metrics,
pub repository: Repository,
pub reset_state: bool,
pub transmitter: Transmitter,
pub transport: Transport,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
pub enum Transport {
Grpc(grpc::Config),
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
pub enum Transmitter {
Nats(nats::Config),
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
pub enum Repository {
Postgres(postgres::Config),
InMemory,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
pub enum Metrics {
Prometheus(metrics::Config),
}

pub fn validate(config: &Config) -> Result<(), Box<dyn std::error::Error>> {
if config.clock_cycle_interval.is_zero() {
return Err("clock cycle interval cannot be zero".into());
}

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

fn config() -> Config {
Config {
automigrate: true,
log_level: log::Level::Info,
clock_cycle_interval: time::Duration::from_millis(100),
metrics: Metrics::Prometheus(metrics::Config {
port: 3000,
endpoint: String::from("/metrics"),
}),
repository: Repository::InMemory,
reset_state: true,
transmitter: Transmitter::Nats(nats::Config {
port: 3001,
host: String::from("127.0.0.1"),
}),
transport: Transport::Grpc(grpc::Config { port: 3002 }),
}
}

#[test]
fn test_validate_config() {
struct TestCase {
name: String,
config: Config,
expected_valid: bool,
}

let test_cases = vec![
TestCase {
name: String::from("valid"),
config: config(),
expected_valid: true,
},
TestCase {
name: String::from("zero duration"),
config: Config {
clock_cycle_interval: time::Duration::from_secs(0),
..config()
},
expected_valid: false,
},
];

for test_case in test_cases {
let valid = validate(&test_case.config).is_ok();
assert_eq!(
valid, test_case.expected_valid,
"test case failed: {}",
test_case.name
);
}
}
}
1 change: 1 addition & 0 deletions src/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ mod tests {
nats_connection: &async_nats::Client,
) -> Arc<TransmissionScheduler> {
let scheduler = scheduler::TransmissionScheduler::new(
time::Duration::from_micros(10),
Arc::new(postgres_repository().await),
Arc::new(nats_publisher(&nats_connection)),
Arc::new(now),
Expand Down
35 changes: 29 additions & 6 deletions src/load_config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use humantime;
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;

use serde::Deserialize;

Expand All @@ -14,6 +16,7 @@ const ENV_POSTGRES_PASSWORD: &'static str = "POSTGRES_PASSWORD";
struct FileConfig {
automigrate: bool,
log_level: String,
clock_cycle_interval: String,
metrics: config::Metrics,
repository: Repository,
reset_state: bool,
Expand Down Expand Up @@ -60,13 +63,27 @@ pub fn load_config(file_path: &str) -> Result<config::Config, Box<dyn Error>> {

let secrets = load_secrets_from_env()?;

Ok(derive_config(config, secrets))
let config = derive_config(config, secrets)?;

config::validate(&config)?;

Ok(config)
}

fn derive_config(config: FileConfig, secrets: EnvConfig) -> config::Config {
config::Config {
fn derive_config(config: FileConfig, secrets: EnvConfig) -> Result<config::Config, Box<dyn Error>> {
let log_level = match log::Level::from_str(&config.log_level) {
Ok(log_level) => log_level,
Err(err) => {
return Err(format!("invalid log level '{}': {err}", &config.log_level).into());
}
};

let clock_cycle_interval = humantime::parse_duration(&config.clock_cycle_interval)?;

Ok(config::Config {
automigrate: config.automigrate,
log_level: config.log_level,
log_level,
clock_cycle_interval,
metrics: config.metrics,
repository: match config.repository {
Repository::Postgres(postgres_config) => {
Expand All @@ -84,12 +101,13 @@ fn derive_config(config: FileConfig, secrets: EnvConfig) -> config::Config {
transmitter: config.transmitter,
transport: config.transport,
reset_state: config.reset_state,
}
})
}

#[cfg(test)]
mod tests {
use super::*;
use std::time;

#[test]
fn test_load_config() {
Expand All @@ -104,7 +122,12 @@ mod tests {
let configuration = load_config(config_file).expect("could not load configuration");

// Merely asserting the log level is enough to assert the structure of the file contents.
assert_eq!(configuration.log_level, "debug".to_string());
assert_eq!(configuration.log_level, log::Level::Debug);

assert_eq!(
configuration.clock_cycle_interval,
time::Duration::from_millis(100)
);

match configuration.repository {
config::Repository::Postgres(postgres_config) => {
Expand Down
8 changes: 5 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ const DEFAULT_CONFIG_FILE_PATH: &'static str = "config.ron";

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();

let args: Vec<String> = std::env::args().collect();
let config_file_path = match args.len() {
1 => DEFAULT_CONFIG_FILE_PATH,
2 => &args[1],
_ => {
println!("Please specify the path to the configuration file as the only argument.");
error!("Please specify the path to the configuration file as the only argument.");
process::exit(1);
}
};
Expand All @@ -44,8 +46,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

// Initialise logger.
let rust_log = "RUST_LOG";
env::set_var(rust_log, config.log_level);
env_logger::init();
env::set_var(rust_log, config.log_level.as_str());
info!("Starting application.");

// Construct transmitter.
Expand Down Expand Up @@ -138,6 +139,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

// Construct scheduler.
let scheduler = Arc::new(scheduler::TransmissionScheduler::new(
config.clock_cycle_interval,
repository,
transmitter,
now_provider,
Expand Down
2 changes: 1 addition & 1 deletion src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use tokio::net::TcpListener;
const METRIC_NAME: &str = "procedure";
const METRIC_HELP_TEXT: &str = "Number of procedure calls";

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
pub struct Config {
pub port: u16,
pub endpoint: String,
Expand Down
2 changes: 1 addition & 1 deletion src/nats.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use log::info;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
pub struct Config {
pub port: u16,
pub host: String,
Expand Down
2 changes: 1 addition & 1 deletion src/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use log::info;
use serde::Deserialize;
use sqlx::postgres::{PgPool, PgPoolOptions};

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
pub struct Config {
pub name: String,
pub host: String,
Expand Down
Loading

0 comments on commit 7083f87

Please sign in to comment.