|
1 | 1 | use serde::Deserialize; |
| 2 | +use std::net::SocketAddr; |
2 | 3 |
|
3 | 4 | #[derive(Deserialize)] |
4 | | -pub(crate) struct Config { |
5 | | - pub(crate) server_config: ServerConfig, |
6 | | - pub(crate) jwt_auth_config: Option<JwtAuthConfig>, |
7 | | - pub(crate) postgresql_config: Option<PostgreSQLConfig>, |
| 5 | +struct Config { |
| 6 | + server_config: ServerConfig, |
| 7 | + jwt_auth_config: Option<JwtAuthConfig>, |
| 8 | + postgresql_config: Option<PostgreSQLConfig>, |
8 | 9 | } |
9 | 10 |
|
10 | 11 | #[derive(Deserialize)] |
11 | | -pub(crate) struct ServerConfig { |
12 | | - pub(crate) host: String, |
13 | | - pub(crate) port: u16, |
| 12 | +struct ServerConfig { |
| 13 | + bind_address: SocketAddr, |
14 | 14 | } |
15 | 15 |
|
16 | 16 | #[derive(Deserialize)] |
17 | | -pub(crate) struct JwtAuthConfig { |
18 | | - pub(crate) rsa_pem: Option<String>, |
| 17 | +struct JwtAuthConfig { |
| 18 | + rsa_pem: Option<String>, |
19 | 19 | } |
20 | 20 |
|
| 21 | +const JWT_RSA_PEM_VAR: &str = "VSS_JWT_RSA_PEM"; |
| 22 | +const PSQL_USER_VAR: &str = "VSS_PSQL_USERNAME"; |
| 23 | +const PSQL_PASS_VAR: &str = "VSS_PSQL_PASSWORD"; |
| 24 | +const PSQL_ADDR_VAR: &str = "VSS_PSQL_ADDRESS"; |
| 25 | +const PSQL_DB_VAR: &str = "VSS_PSQL_DEFAULT_DB"; |
| 26 | +const PSQL_VSS_DB_VAR: &str = "VSS_PSQL_VSS_DB"; |
| 27 | +const PSQL_TLS_VAR: &str = "VSS_PSQL_TLS"; |
| 28 | +const PSQL_CERT_PEM_VAR: &str = "VSS_PSQL_CRT_PEM"; |
| 29 | + |
21 | 30 | #[derive(Deserialize)] |
22 | | -pub(crate) struct PostgreSQLConfig { |
23 | | - pub(crate) username: Option<String>, // Optional in TOML, can be overridden by env |
24 | | - pub(crate) password: Option<String>, // Optional in TOML, can be overridden by env |
25 | | - pub(crate) host: String, |
26 | | - pub(crate) port: u16, |
27 | | - pub(crate) default_database: String, |
28 | | - pub(crate) vss_database: String, |
29 | | - pub(crate) tls: Option<TlsConfig>, |
| 31 | +struct PostgreSQLConfig { |
| 32 | + username: Option<String>, |
| 33 | + password: Option<String>, |
| 34 | + address: Option<SocketAddr>, |
| 35 | + default_database: Option<String>, |
| 36 | + vss_database: Option<String>, |
| 37 | + tls: Option<TlsConfig>, |
30 | 38 | } |
31 | 39 |
|
32 | 40 | #[derive(Deserialize)] |
33 | | -pub(crate) struct TlsConfig { |
34 | | - pub(crate) crt_pem: Option<String>, |
| 41 | +struct TlsConfig { |
| 42 | + crt_pem: Option<String>, |
35 | 43 | } |
36 | 44 |
|
37 | | -impl PostgreSQLConfig { |
38 | | - pub(crate) fn to_postgresql_endpoint(&self) -> String { |
39 | | - let username_env = std::env::var("VSS_POSTGRESQL_USERNAME"); |
40 | | - let username = username_env.as_ref() |
41 | | - .ok() |
42 | | - .or_else(|| self.username.as_ref()) |
43 | | - .expect("PostgreSQL database username must be provided in config or env var VSS_POSTGRESQL_USERNAME must be set."); |
44 | | - let password_env = std::env::var("VSS_POSTGRESQL_PASSWORD"); |
45 | | - let password = password_env.as_ref() |
46 | | - .ok() |
47 | | - .or_else(|| self.password.as_ref()) |
48 | | - .expect("PostgreSQL database password must be provided in config or env var VSS_POSTGRESQL_PASSWORD must be set."); |
49 | | - |
50 | | - format!("postgresql://{}:{}@{}:{}", username, password, self.host, self.port) |
51 | | - } |
| 45 | +pub(crate) struct Configuration { |
| 46 | + pub(crate) bind_address: SocketAddr, |
| 47 | + pub(crate) rsa_pem: Option<String>, |
| 48 | + pub(crate) postgresql_prefix: String, |
| 49 | + pub(crate) default_db: String, |
| 50 | + pub(crate) vss_db: String, |
| 51 | + pub(crate) tls_config: Option<Option<String>>, |
52 | 52 | } |
53 | 53 |
|
54 | | -pub(crate) fn load_config(config_path: &str) -> Result<Config, Box<dyn std::error::Error>> { |
55 | | - let config_str = std::fs::read_to_string(config_path)?; |
56 | | - let config: Config = toml::from_str(&config_str)?; |
57 | | - Ok(config) |
| 54 | +pub(crate) fn load_configuration(config_file_path: &str) -> Result<Configuration, String> { |
| 55 | + let config_file = std::fs::read_to_string(config_file_path) |
| 56 | + .map_err(|e| format!("Failed to read configuration file: {}", e))?; |
| 57 | + let Config { server_config: ServerConfig { bind_address }, jwt_auth_config, postgresql_config } = |
| 58 | + toml::from_str(&config_file) |
| 59 | + .map_err(|e| format!("Failed to parse configuration file: {}", e))?; |
| 60 | + |
| 61 | + macro_rules! read_env { |
| 62 | + ($env_var:expr) => { |
| 63 | + match std::env::var($env_var) { |
| 64 | + Ok(env) => Some(env), |
| 65 | + Err(std::env::VarError::NotPresent) => None, |
| 66 | + Err(e) => { |
| 67 | + return Err(format!( |
| 68 | + "Failed to load the {} environment variable: {}", |
| 69 | + $env_var, e |
| 70 | + )) |
| 71 | + }, |
| 72 | + } |
| 73 | + }; |
| 74 | + } |
| 75 | + |
| 76 | + macro_rules! read_config { |
| 77 | + ($env:expr, $config: expr, $item: expr, $var_name: expr) => { |
| 78 | + $env.or($config).ok_or(format!( |
| 79 | + "{} must be provided in the configuration file or the environment variable {} must be set.", |
| 80 | + $item, $var_name |
| 81 | + ))? |
| 82 | + }; |
| 83 | + } |
| 84 | + |
| 85 | + let rsa_pem_env = read_env!(JWT_RSA_PEM_VAR); |
| 86 | + let rsa_pem = rsa_pem_env.or(jwt_auth_config.and_then(|config| config.rsa_pem)); |
| 87 | + |
| 88 | + let username_env = read_env!(PSQL_USER_VAR); |
| 89 | + let password_env = read_env!(PSQL_PASS_VAR); |
| 90 | + let address_env: Option<SocketAddr> = read_env!(PSQL_ADDR_VAR) |
| 91 | + .map(|address| { |
| 92 | + address.parse().map_err(|e| { |
| 93 | + format!("Unable to parse the postgresql address environment variable: {}", e) |
| 94 | + }) |
| 95 | + }) |
| 96 | + .transpose()?; |
| 97 | + let default_db_env = read_env!(PSQL_DB_VAR); |
| 98 | + let vss_db_env = read_env!(PSQL_VSS_DB_VAR); |
| 99 | + let tls_config_env = read_env!(PSQL_TLS_VAR); |
| 100 | + let crt_pem_env = read_env!(PSQL_CERT_PEM_VAR); |
| 101 | + |
| 102 | + let ( |
| 103 | + username_config, |
| 104 | + password_config, |
| 105 | + address_config, |
| 106 | + default_db_config, |
| 107 | + vss_db_config, |
| 108 | + tls_config, |
| 109 | + ) = match postgresql_config { |
| 110 | + Some(c) => ( |
| 111 | + c.username, |
| 112 | + c.password, |
| 113 | + c.address, |
| 114 | + c.default_database, |
| 115 | + c.vss_database, |
| 116 | + c.tls.map(|tls| tls.crt_pem), |
| 117 | + ), |
| 118 | + None => (None, None, None, None, None, None), |
| 119 | + }; |
| 120 | + |
| 121 | + let username = |
| 122 | + read_config!(username_env, username_config, "PostgreSQL database username", PSQL_USER_VAR); |
| 123 | + let password = |
| 124 | + read_config!(password_env, password_config, "PostgreSQL database password", PSQL_PASS_VAR); |
| 125 | + let address = |
| 126 | + read_config!(address_env, address_config, "PostgreSQL service address", PSQL_ADDR_VAR); |
| 127 | + let default_db = read_config!( |
| 128 | + default_db_env, |
| 129 | + default_db_config, |
| 130 | + "PostgreSQL default database name", |
| 131 | + PSQL_DB_VAR |
| 132 | + ); |
| 133 | + let vss_db = |
| 134 | + read_config!(vss_db_env, vss_db_config, "PostgreSQL vss database name", PSQL_VSS_DB_VAR); |
| 135 | + |
| 136 | + let tls_config = |
| 137 | + crt_pem_env.map(|pem| Some(pem)).or(tls_config_env.map(|_| None)).or(tls_config); |
| 138 | + |
| 139 | + let postgresql_prefix = format!("postgresql://{}:{}@{}", username, password, address); |
| 140 | + |
| 141 | + Ok(Configuration { bind_address, rsa_pem, postgresql_prefix, default_db, vss_db, tls_config }) |
58 | 142 | } |
0 commit comments