diff --git a/config/maximal-config-example.toml b/config/maximal-config-example.toml index 92ef98a93..32582281f 100644 --- a/config/maximal-config-example.toml +++ b/config/maximal-config-example.toml @@ -33,6 +33,13 @@ port = 7300 # that is used by the `indexer-agent`. It is expected that `indexer-agent` will create # the necessary tables. postgres_url = "postgres://postgres@postgres:5432/postgres" +# You can also use the following fields to specify the connection details separately. +# either use `postgres_url` or the following fields. +# host = "postgres-host" +# port = 5432 +# user = "user" +# password = "pwd" +# database = "postgres" [graph_node] # URL to your graph-node's query endpoint diff --git a/config/minimal-config-example.toml b/config/minimal-config-example.toml index 42d24ffe6..3cc0bda45 100644 --- a/config/minimal-config-example.toml +++ b/config/minimal-config-example.toml @@ -19,6 +19,13 @@ operator_mnemonic = "celery smart tip orange scare van steel radio dragon joy al # that is used by the `indexer-agent`. It is expected that `indexer-agent` will create # the necessary tables. postgres_url = "postgres://postgres@postgres:5432/postgres" +# You can also use the following fields to specify the connection details separately. +# either use `postgres_url` or the following fields. +# host = "postgres-host" +# port = 5432 +# user = "user" +# password = "pwd" +# database = "postgres" [graph_node] # URL to your graph-node's query endpoint diff --git a/config/src/config.rs b/config/src/config.rs index f3ae6fb60..6c0f3f611 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -147,8 +147,44 @@ pub struct IndexerConfig { #[derive(Debug, Deserialize)] #[cfg_attr(test, derive(PartialEq))] -pub struct DatabaseConfig { - pub postgres_url: Url, +#[serde(untagged)] +#[serde(deny_unknown_fields)] +pub enum DatabaseConfig { + PostgresUrl { + postgres_url: Url, + }, + PostgresVars { + host: String, + port: Option, + user: String, + password: Option, + database: String, + }, +} +impl DatabaseConfig { + pub fn get_formated_postgres_url(self) -> Url { + match self { + DatabaseConfig::PostgresUrl { postgres_url } => postgres_url, + DatabaseConfig::PostgresVars { + host, + port, + user, + password, + database, + } => { + let postgres_url_str = format!("postgres://{}@{}/{}", user, host, database); + let mut postgres_url = + Url::parse(&postgres_url_str).expect("Failed to parse database_url"); + postgres_url + .set_password(password.as_deref()) + .expect("Failed to set password for database"); + postgres_url + .set_port(port) + .expect("Failed to set port for database"); + postgres_url + } + } + } } #[derive(Debug, Deserialize)] @@ -286,6 +322,8 @@ mod tests { use crate::{Config, ConfigPrefix}; + use super::DatabaseConfig; + #[test] fn test_minimal_config() { Config::parse( @@ -398,4 +436,89 @@ mod tests { test_value ); } + #[test] + fn test_url_format() { + let data = DatabaseConfig::PostgresVars { + host: String::from("postgres"), + port: Some(1234), + user: String::from("postgres"), + password: Some(String::from("postgres")), + database: String::from("postgres"), + }; + let formated_data = data.get_formated_postgres_url(); + assert_eq!( + formated_data.as_str(), + "postgres://postgres:postgres@postgres:1234/postgres" + ); + + let data = DatabaseConfig::PostgresVars { + host: String::from("postgres"), + port: None, + user: String::from("postgres"), + password: None, + database: String::from("postgres"), + }; + let formated_data = data.get_formated_postgres_url(); + assert_eq!( + formated_data.as_str(), + "postgres://postgres@postgres/postgres" + ); + } + + // Test that we can fill in mandatory config fields missing from the config file with + // environment variables + #[sealed_test(files = ["minimal-config-example.toml"])] + fn test_change_db_config_with_individual_vars() { + let mut minimal_config: toml::Value = toml::from_str( + fs::read_to_string("minimal-config-example.toml") + .unwrap() + .as_str(), + ) + .unwrap(); + // Remove the database.postgres_url field from minimal config + minimal_config + .get_mut("database") + .unwrap() + .as_table_mut() + .unwrap() + .remove("postgres_url"); + + let database_table = minimal_config + .get_mut("database") + .unwrap() + .as_table_mut() + .unwrap(); + database_table.insert( + "host".to_string(), + toml::Value::String("postgres".to_string()), + ); + database_table.insert( + "user".to_string(), + toml::Value::String("postgres".to_string()), + ); + database_table.insert( + "database".to_string(), + toml::Value::String("postgres".to_string()), + ); + + // Save the modified minimal config to a named temporary file using tempfile + let temp_minimal_config_path = tempfile::NamedTempFile::new().unwrap(); + fs::write( + temp_minimal_config_path.path(), + toml::to_string(&minimal_config).unwrap(), + ) + .unwrap(); + + // Parse the config with new datbase vars + let config = Config::parse( + ConfigPrefix::Service, + &PathBuf::from(temp_minimal_config_path.path()), + ) + .unwrap(); + + assert_eq!( + config.database.get_formated_postgres_url().as_str(), + "postgres://postgres@postgres/postgres" + ); + } } diff --git a/service/src/config.rs b/service/src/config.rs index df19d5502..b904bd40d 100644 --- a/service/src/config.rs +++ b/service/src/config.rs @@ -30,7 +30,7 @@ impl From for Config { free_query_auth_token: value.service.free_query_auth_token, }, database: DatabaseConfig { - postgres_url: value.database.postgres_url.into(), + postgres_url: value.database.get_formated_postgres_url().to_string(), }, graph_node: Some(GraphNodeConfig { status_url: value.graph_node.status_url.into(), diff --git a/tap-agent/src/config.rs b/tap-agent/src/config.rs index 830708e7c..2379de834 100644 --- a/tap-agent/src/config.rs +++ b/tap-agent/src/config.rs @@ -37,7 +37,7 @@ impl From for Config { log_level: None, }, postgres: Postgres { - postgres_url: value.database.postgres_url, + postgres_url: value.database.get_formated_postgres_url(), }, network_subgraph: NetworkSubgraph { network_subgraph_deployment: value.subgraphs.network.config.deployment_id,