From 8d8bfd0edeca6bf762ca19c67dea9baac3772d6f Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Fri, 10 Feb 2023 10:07:44 +0100 Subject: [PATCH] [New] Allow to override DB config in env --- .../li/strolch/runtime/StrolchConstants.java | 9 +- .../configuration/DbConnectionBuilder.java | 84 +++++++++++++------ .../postgresql/PostgreSqlInitializer.java | 2 +- .../xml/XmlPersistenceHandler.java | 39 +++++++-- .../main/java/li/strolch/db/DbConstants.java | 2 + 5 files changed, 101 insertions(+), 35 deletions(-) diff --git a/agent/src/main/java/li/strolch/runtime/StrolchConstants.java b/agent/src/main/java/li/strolch/runtime/StrolchConstants.java index 3098a4b6a..ee69ea397 100644 --- a/agent/src/main/java/li/strolch/runtime/StrolchConstants.java +++ b/agent/src/main/java/li/strolch/runtime/StrolchConstants.java @@ -16,6 +16,7 @@ package li.strolch.runtime; import static li.strolch.utils.helper.StringHelper.DOT; +import static li.strolch.utils.helper.StringHelper.UNDERLINE; import li.strolch.agent.api.ObserverHandler; import li.strolch.model.StrolchModelConstants; @@ -46,10 +47,14 @@ public class StrolchConstants extends StrolchModelConstants { public static final String TYPE_STROLCH_JOB = "StrolchJob"; public static String makeRealmKey(String realmName, String key) { + return makeRealmKey(realmName, key, false); + } + + public static String makeRealmKey(String realmName, String key, boolean useEnv) { String realmKey = key; if (!realmName.equals(DEFAULT_REALM)) - realmKey += DOT + realmName; - return realmKey; + realmKey += (useEnv ? UNDERLINE : DOT) + realmName; + return useEnv ? realmKey.replace(DOT, UNDERLINE).toUpperCase() : realmKey; } /** diff --git a/agent/src/main/java/li/strolch/runtime/configuration/DbConnectionBuilder.java b/agent/src/main/java/li/strolch/runtime/configuration/DbConnectionBuilder.java index da18feed0..a0f1c0b26 100644 --- a/agent/src/main/java/li/strolch/runtime/configuration/DbConnectionBuilder.java +++ b/agent/src/main/java/li/strolch/runtime/configuration/DbConnectionBuilder.java @@ -16,7 +16,9 @@ package li.strolch.runtime.configuration; import static li.strolch.db.DbConstants.*; +import static li.strolch.runtime.StrolchConstants.DEFAULT_REALM; import static li.strolch.runtime.StrolchConstants.makeRealmKey; +import static li.strolch.utils.helper.StringHelper.*; import javax.sql.DataSource; import java.sql.Connection; @@ -29,7 +31,6 @@ import java.util.Set; import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchRealm; import li.strolch.persistence.api.StrolchPersistenceException; -import li.strolch.runtime.StrolchConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,36 +62,44 @@ public abstract class DbConnectionBuilder { if (realm.getMode().isTransient()) continue; - String dbIgnoreRealmKey = makeRealmKey(realmName, PROP_DB_IGNORE_REALM); - String dbUrlKey = makeRealmKey(realmName, PROP_DB_URL); - String dbUsernameKey = makeRealmKey(realmName, PROP_DB_USERNAME); - String dbPasswordKey = makeRealmKey(realmName, PROP_DB_PASSWORD); + String dbUseEnvKey = makeRealmKey(realmName, PROP_USE_ENV, false); + boolean dbUseEnv = this.configuration.getBoolean(dbUseEnvKey, false); + if (dbUseEnv) + logger.info("Configuration specifies to use environment variables to configure DB access..."); - boolean dbIgnoreRealm = this.configuration.getBoolean(dbIgnoreRealmKey, Boolean.FALSE); + String dbUrlKey = makeRealmKey(realmName, PROP_DB_URL, dbUseEnv); + String dbUsernameKey = makeRealmKey(realmName, PROP_DB_USERNAME, dbUseEnv); + String dbPasswordKey = makeRealmKey(realmName, PROP_DB_PASSWORD, dbUseEnv); + + String dbIgnoreRealmKey = makeRealmKey(realmName, PROP_DB_IGNORE_REALM, false); + boolean dbIgnoreRealm = this.configuration.getBoolean(dbIgnoreRealmKey, false); if (dbIgnoreRealm) { logger.info("[" + realm + "] Ignoring any DB configuration for Realm " + realmName); continue; } - String dbUrl = this.configuration.getString(dbUrlKey, null); - String username = this.configuration.getString(dbUsernameKey, null); - String password = this.configuration.getString(dbPasswordKey, null); + String dbUrl = getConfigString(dbUrlKey, dbUseEnv); + String username = getConfigString(dbUsernameKey, dbUseEnv); + String password = getConfigString(dbPasswordKey, dbUseEnv); if (this.configuration.getBoolean(PROP_DB_ALLOW_HOST_OVERRIDE_ENV, false) // && System.getProperties().containsKey(PROP_DB_HOST_OVERRIDE)) { - dbUrl = overridePostgresqlHost(realm.getRealm(), dbUrl); + dbUrl = overridePostgresqlHost(realm.getRealm(), dbUrl, dbUseEnv); } // find any pool configuration values - Set propertyKeys = this.configuration.getPropertyKeys(); + Map properties = dbUseEnv ? System.getenv() : this.configuration.getAsMap(); + String dbPoolPrefix = dbUseEnv ? + PROP_DB_POOL_PREFIX.replace(DOT, UNDERLINE).toUpperCase() : + PROP_DB_POOL_PREFIX; Properties props = new Properties(); - for (String key : propertyKeys) { - if (!key.startsWith(PROP_DB_POOL_PREFIX)) + for (String key : properties.keySet()) { + if (!key.startsWith(dbPoolPrefix)) continue; // TODO we should change how properties for realms are configured // since defaultRealm does not have to be on the key, we need this hack: - String[] segments = key.split("\\."); + String[] segments = key.split(dbUseEnv ? UNDERLINE : "\\."); String poolKey; String foundRealm; if (segments.length == 4) { @@ -98,7 +107,7 @@ public abstract class DbConnectionBuilder { foundRealm = segments[3]; } else if (segments.length == 3) { // default realm - foundRealm = StrolchConstants.DEFAULT_REALM; + foundRealm = DEFAULT_REALM; } else { throw new IllegalArgumentException("Can't detect realm of this property: " + key); } @@ -107,39 +116,62 @@ public abstract class DbConnectionBuilder { continue; poolKey = segments[2]; - String value = this.configuration.getString(key, null); + String value = properties.get(key); props.setProperty(poolKey, value); } - DataSource ds = build(realmName, dbUrl, username, password, props); - - dsMap.put(realmName, ds); + DataSource dataSource = build(realmName, dbUrl, username, password, props); + dsMap.put(realmName, dataSource); } return dsMap; } - public static String overridePostgresqlHost(String realm, String dbUrl) { - if (!System.getProperties().containsKey(PROP_DB_HOST_OVERRIDE)) - return dbUrl; + private String getConfigString(String dbKey, boolean useEnv) { + if (!useEnv) + return this.configuration.getString(dbKey, null); + + String value = System.getenv(dbKey); + if (isEmpty(value)) + throw new IllegalStateException("Missing environment variable " + dbKey); + return value; + } + + public static String overridePostgresqlHost(String realmName, String dbUrl) { + return overridePostgresqlHost(realmName, dbUrl, false); + } + + public static String overridePostgresqlHost(String realmName, String dbUrl, boolean useEnv) { + String hostOverride; + if (useEnv) { + if (!System.getenv().containsKey(ENV_DB_HOST_OVERRIDE)) + return dbUrl; + String hostOverrideKey = makeRealmKey(realmName, PROP_DB_HOST_OVERRIDE, true); + hostOverride = System.getenv(hostOverrideKey); + } else { + if (!System.getProperties().containsKey(PROP_DB_HOST_OVERRIDE)) + return dbUrl; + String hostOverrideKey = makeRealmKey(realmName, PROP_DB_HOST_OVERRIDE, false); + hostOverride = System.getProperty(hostOverrideKey); + } + if (!dbUrl.startsWith("jdbc:postgresql://")) throw new IllegalStateException("DB URL is invalid: " + dbUrl); String tmp = dbUrl.substring("jdbc:postgresql://".length()); String host = tmp.substring(0, tmp.indexOf('/')); String dbName = tmp.substring(tmp.indexOf('/')); - String hostOverride = System.getProperty(PROP_DB_HOST_OVERRIDE); if (host.equals(hostOverride)) return dbUrl; - logger.warn("[" + realm + "] Replacing db host " + host + " with override " + hostOverride); + logger.warn("[" + realmName + "] Replacing db host " + host + " with override " + hostOverride); dbUrl = "jdbc:postgresql://" + hostOverride + dbName; - logger.warn("[" + realm + "] DB URL is now " + dbUrl); + logger.warn("[" + realmName + "] DB URL is now " + dbUrl); return dbUrl; } - protected abstract DataSource build(String realm, String url, String username, String password, Properties proops); + protected abstract DataSource build(String realm, String url, String username, String password, Properties props); protected void validateConnection(DataSource ds) { try (Connection con = ds.getConnection()) { diff --git a/persistence-postgresql/src/main/java/li/strolch/persistence/postgresql/PostgreSqlInitializer.java b/persistence-postgresql/src/main/java/li/strolch/persistence/postgresql/PostgreSqlInitializer.java index c725812d1..2a20f3ae7 100644 --- a/persistence-postgresql/src/main/java/li/strolch/persistence/postgresql/PostgreSqlInitializer.java +++ b/persistence-postgresql/src/main/java/li/strolch/persistence/postgresql/PostgreSqlInitializer.java @@ -91,7 +91,7 @@ public abstract class PostgreSqlInitializer extends SystemAction { protected File getDataStoreFile(RuntimeConfiguration runtimeConfiguration, ComponentConfiguration realmConfiguration, String realmName) { - String dataStoreKey = makeRealmKey(realmName, PREFIX_DATA_STORE_FILE); + String dataStoreKey = makeRealmKey(realmName, PREFIX_DATA_STORE_FILE, false); return realmConfiguration.getDataFile(dataStoreKey, null, runtimeConfiguration, true); } } diff --git a/persistence-xml/src/main/java/li/strolch/persistence/xml/XmlPersistenceHandler.java b/persistence-xml/src/main/java/li/strolch/persistence/xml/XmlPersistenceHandler.java index 103520056..013010e88 100644 --- a/persistence-xml/src/main/java/li/strolch/persistence/xml/XmlPersistenceHandler.java +++ b/persistence-xml/src/main/java/li/strolch/persistence/xml/XmlPersistenceHandler.java @@ -16,7 +16,10 @@ package li.strolch.persistence.xml; import static li.strolch.agent.impl.DefaultRealmHandler.PREFIX_DATA_STORE_FILE; +import static li.strolch.db.DbConstants.PROP_DB_IGNORE_REALM; +import static li.strolch.db.DbConstants.PROP_USE_ENV; import static li.strolch.runtime.StrolchConstants.makeRealmKey; +import static li.strolch.utils.helper.StringHelper.isEmpty; import java.io.File; import java.text.MessageFormat; @@ -42,6 +45,7 @@ import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.runtime.configuration.RuntimeConfiguration; import li.strolch.runtime.configuration.StrolchConfiguration; import li.strolch.runtime.configuration.StrolchConfigurationException; +import li.strolch.utils.helper.StringHelper; import li.strolch.xmlpers.api.*; /** @@ -79,18 +83,21 @@ public class XmlPersistenceHandler extends StrolchComponent implements Persisten if (realm.getMode().isTransient()) continue; - String dbIgnoreRealmKey = makeRealmKey(realmName, PROP_DB_IGNORE_REALM); - String dbStorePathKey = makeRealmKey(realmName, PROP_DB_STORE_PATH); - String dbVerboseKey = makeRealmKey(realmName, PROP_VERBOSE); + String dbUseEnvKey = makeRealmKey(realmName, PROP_USE_ENV, false); + boolean dbUseEnv = configuration.getBoolean(dbUseEnvKey, false); - boolean dbIgnoreRealm = configuration.getBoolean(dbIgnoreRealmKey, Boolean.FALSE); + String dbStorePathKey = makeRealmKey(realmName, PROP_DB_STORE_PATH, dbUseEnv); + String dbVerboseKey = makeRealmKey(realmName, PROP_VERBOSE, dbUseEnv); + + String dbIgnoreRealmKey = makeRealmKey(realmName, PROP_DB_IGNORE_REALM, false); + boolean dbIgnoreRealm = configuration.getBoolean(dbIgnoreRealmKey, false); if (dbIgnoreRealm) { logger.info("Ignoring any DB configuration for Realm " + realmName); continue; } - String dbStorePath = configuration.getString(dbStorePathKey, null); - boolean verbose = configuration.getBoolean(dbVerboseKey, Boolean.FALSE); + String dbStorePath = getConfigString(configuration, dbStorePathKey, dbUseEnv); + boolean verbose = getConfigBoolean(configuration, dbVerboseKey, dbUseEnv); // validate URL if (dbStorePaths.contains(dbStorePath)) @@ -128,6 +135,26 @@ public class XmlPersistenceHandler extends StrolchComponent implements Persisten super.initialize(configuration); } + private String getConfigString(ComponentConfiguration configuration, String dbKey, boolean useEnv) { + if (!useEnv) + return configuration.getString(dbKey, null); + + String value = System.getenv(dbKey); + if (isEmpty(value)) + throw new IllegalStateException("Missing environment variable " + dbKey); + return value; + } + + private Boolean getConfigBoolean(ComponentConfiguration configuration, String dbKey, boolean useEnv) { + if (!useEnv) + return configuration.getBoolean(dbKey, false); + + String value = System.getenv(dbKey); + if (isEmpty(value)) + throw new IllegalStateException("Missing environment variable " + dbKey); + return StringHelper.parseBoolean(value); + } + @Override public void start() throws Exception { diff --git a/utils/src/main/java/li/strolch/db/DbConstants.java b/utils/src/main/java/li/strolch/db/DbConstants.java index 17049c4a2..2557f8be3 100644 --- a/utils/src/main/java/li/strolch/db/DbConstants.java +++ b/utils/src/main/java/li/strolch/db/DbConstants.java @@ -35,4 +35,6 @@ public class DbConstants { public static final String PROP_ALLOW_DATA_INIT_ON_EMPTY_DB = "allowDataInitOnEmptyDb"; public static final String PROP_DB_VERSION = "db_version"; public static final String RESOURCE_DB_VERSION = "/{0}_db_version.properties"; + + public static final String ENV_DB_HOST_OVERRIDE = "DB_HOST_OVERRIDE"; }