Skip to content

Commit

Permalink
Env variables resolution moved to defaultOverrides
Browse files Browse the repository at this point in the history
  • Loading branch information
andreaTP committed Mar 4, 2019
1 parent 30b6556 commit 7fe4788
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 117 deletions.
12 changes: 11 additions & 1 deletion build.sbt
Expand Up @@ -85,7 +85,17 @@ lazy val configLib = Project("config", file("config"))
Test/ run / fork := true

//env vars for tests
Test / envVars ++= Map("testList.0" -> "0", "testList.1" -> "1")
Test / envVars ++= Map("testList.0" -> "0",
"testList.1" -> "1",
"CONFIG_FORCE_b" -> "5",
"CONFIG_FORCE_testList_0" -> "10",
"CONFIG_FORCE_testList_1" -> "11",
"CONFIG_FORCE_42___a" -> "1",
"CONFIG_FORCE_a_b_c" -> "2",
"CONFIG_FORCE_a__c" -> "3",
"CONFIG_FORCE_a___c" -> "4",
"CONFIG_FORCE_akka_version" -> "foo",
"CONFIG_FORCE_akka_event__handler__dispatcher_max__pool__size" -> "10")

OsgiKeys.exportPackage := Seq("com.typesafe.config", "com.typesafe.config.impl")
publish := sys.error("use publishSigned instead of plain publish")
Expand Down
59 changes: 57 additions & 2 deletions config/src/main/java/com/typesafe/config/ConfigFactory.java
Expand Up @@ -36,6 +36,7 @@
*/
public final class ConfigFactory {
private static final String STRATEGY_PROPERTY_NAME = "config.strategy";
private static final String OVERRIDE_WITH_ENV_PROPERTY_NAME = "config.override_with_env_vars";

private ConfigFactory() {
}
Expand Down Expand Up @@ -383,7 +384,11 @@ public static Config defaultReference(ClassLoader loader) {
* @return the default override configuration
*/
public static Config defaultOverrides() {
return systemProperties();
if (!getOverrideWithEnv()) {
return systemProperties();
} else {
return systemEnvironmentOverrides().withFallback(systemProperties());
}
}

/**
Expand All @@ -394,7 +399,7 @@ public static Config defaultOverrides() {
* @return the default override configuration
*/
public static Config defaultOverrides(ClassLoader loader) {
return systemProperties();
return defaultOverrides();
}

/**
Expand Down Expand Up @@ -549,6 +554,50 @@ public static Config systemProperties() {
return ConfigImpl.systemPropertiesAsConfig();
}

/**
* Gets a <code>Config</code> containing the system's environment variables
* used to override configuration keys.
* Environment variables taken in considerations are starting with
* {@code CONFIG_FORCE_}
*
* <p>
* Environment variables are mangled in the following way after stripping the prefix "CONFIG_FORCE_":
* <table border="1">
* <tr>
* <th bgcolor="silver">Env Var</th>
* <th bgcolor="silver">Config</th>
* </tr>
* <tr>
* <td>_&nbsp;&nbsp;&nbsp;[1 underscore]</td>
* <td>. [dot]</td>
* </tr>
* <tr>
* <td>__&nbsp;&nbsp;[2 underscore]</td>
* <td>- [dash]</td>
* </tr>
* <tr>
* <td>___&nbsp;[3 underscore]</td>
* <td>_ [underscore]</td>
* </tr>
* </table>
*
* <p>
* A variable like: {@code CONFIG_FORCE_a_b__c___d}
* is translated to a config key: {@code a.b-c_d}
*
* <p>
* This method can return a global immutable singleton, so it's preferred
* over parsing system properties yourself.
* <p>
* {@link #defaultOverrides} will include the system system environment variables as
* overrides if `config.override_with_env_vars` is set to `true`.
*
* @return system environment variable overrides parsed into a <code>Config</code>
*/
public static Config systemEnvironmentOverrides() {
return ConfigImpl.envVariablesOverridesAsConfig();
}

/**
* Gets a <code>Config</code> containing the system's environment variables.
* This method can return a global immutable singleton.
Expand Down Expand Up @@ -1063,4 +1112,10 @@ private static ConfigLoadingStrategy getConfigLoadingStrategy() {
return new DefaultConfigLoadingStrategy();
}
}

private static Boolean getOverrideWithEnv() {
String overrideWithEnv = System.getProperties().getProperty(OVERRIDE_WITH_ENV_PROPERTY_NAME);

return Boolean.parseBoolean(overrideWithEnv);
}
}

This file was deleted.

52 changes: 52 additions & 0 deletions config/src/main/java/com/typesafe/config/impl/ConfigImpl.java
Expand Up @@ -32,6 +32,7 @@
* For use only by the {@link com.typesafe.config} package.
*/
public class ConfigImpl {
private static final String ENV_VAR_OVERRIDE_PREFIX = "CONFIG_FORCE_";

private static class LoaderCache {
private Config currentSystemProperties;
Expand Down Expand Up @@ -360,6 +361,57 @@ public static void reloadEnvVariablesConfig() {
EnvVariablesHolder.envVariables = loadEnvVariables();
}

private static AbstractConfigObject loadEnvVariablesOverrides() {
Map<String, String> env = new HashMap(System.getenv());
Map<String, String> result = new HashMap(System.getenv());
for (String key : env.keySet()) {
if (key.startsWith(ENV_VAR_OVERRIDE_PREFIX)) {
StringBuilder builder = new StringBuilder();

String strippedPrefix = key.substring(ENV_VAR_OVERRIDE_PREFIX.length(), key.length());

int underscores = 0;
for (char c : strippedPrefix.toCharArray()) {
if (c == '_') {
underscores++;
} else {
switch (underscores) {
case 1: builder.append('.');
break;
case 2: builder.append('-');
break;
case 3: builder.append('_');
break;
}
underscores = 0;
builder.append(c);
}
}

String propertyKey = builder.toString();
result.put(propertyKey, env.get(key));
}
}

return PropertiesParser.fromStringMap(newSimpleOrigin("env variables overrides"), result);
}

private static class EnvVariablesOverridesHolder {
static volatile AbstractConfigObject envVariables = loadEnvVariablesOverrides();
}

static AbstractConfigObject envVariablesOverridesAsConfigObject() {
try {
return EnvVariablesOverridesHolder.envVariables;
} catch (ExceptionInInitializerError e) {
throw ConfigImplUtil.extractInitializerError(e);
}
}

public static Config envVariablesOverridesAsConfig() {
return envVariablesOverridesAsConfigObject().toConfig();
}

public static Config defaultReference(final ClassLoader loader) {
return computeCachedConfig(loader, "defaultReference", new Callable<Config>() {
@Override
Expand Down
20 changes: 2 additions & 18 deletions config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
Expand Up @@ -1092,15 +1092,7 @@ class ConfigTest extends TestUtils {

@Test
def testLoadWithEnvSubstitutions() {
TestEnvFirstStrategy.putEnvVar("CONFIG_42___a", "1")
TestEnvFirstStrategy.putEnvVar("CONFIG_a_b_c", "2")
TestEnvFirstStrategy.putEnvVar("CONFIG_a__c", "3")
TestEnvFirstStrategy.putEnvVar("CONFIG_a___c", "4")

TestEnvFirstStrategy.putEnvVar("CONFIG_akka_version", "foo")
TestEnvFirstStrategy.putEnvVar("CONFIG_akka_event__handler__dispatcher_max__pool__size", "10")

System.setProperty("config.strategy", classOf[EnvFirstConfigLoadingStrategy].getCanonicalName)
System.setProperty("config.override_with_env_vars", "true")

try {
val loader02 = new TestClassLoader(this.getClass().getClassLoader(),
Expand All @@ -1125,15 +1117,7 @@ class ConfigTest extends TestUtils {
assertEquals("foo", conf04.getString("akka.version"))
assertEquals(10, conf04.getInt("akka.event-handler-dispatcher.max-pool-size"))
} finally {
System.clearProperty("config.strategy")

TestEnvFirstStrategy.removeEnvVar("CONFIG_42___a")
TestEnvFirstStrategy.removeEnvVar("CONFIG_a_b_c")
TestEnvFirstStrategy.removeEnvVar("CONFIG_a__c")
TestEnvFirstStrategy.removeEnvVar("CONFIG_a___c")

TestEnvFirstStrategy.removeEnvVar("CONFIG_akka_version")
TestEnvFirstStrategy.removeEnvVar("CONFIG_akka_event__handler__dispatcher_max__pool__size")
System.clearProperty("config.override_with_env_vars")
}
}

Expand Down
25 changes: 8 additions & 17 deletions config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
Expand Up @@ -654,24 +654,22 @@ class PublicApiTest extends TestUtils {
}

@Test
def supportsEnvFirstConfigLoadingStrategy(): Unit = {
assertEquals("config.strategy is not set", null, System.getProperty("config.strategy"))
def loadEnvironmentVariablesOverridesIfConfigured(): Unit = {
assertEquals("config.override_with_env_vars is not set", null, System.getProperty("config.override_with_env_vars"))

TestEnvFirstStrategy.putEnvVar("CONFIG_a", "5")
System.setProperty("config.strategy", classOf[EnvFirstConfigLoadingStrategy].getCanonicalName)
System.setProperty("config.override_with_env_vars", "true")

try {
val loaderA1 = new TestClassLoader(this.getClass().getClassLoader(),
Map("reference.conf" -> resourceFile("a_1.conf").toURI.toURL()))
val loaderB2 = new TestClassLoader(this.getClass().getClassLoader(),
Map("reference.conf" -> resourceFile("b_2.conf").toURI.toURL()))

val configA1 = withContextClassLoader(loaderA1) {
val configB2 = withContextClassLoader(loaderB2) {
ConfigFactory.load()
}

assertEquals(5, configA1.getInt("a"))
assertEquals(5, configB2.getInt("b"))
} finally {
System.clearProperty("config.strategy")
TestEnvFirstStrategy.removeEnvVar("CONFIG_a")
System.clearProperty("config.override_with_env_vars")
}
}

Expand Down Expand Up @@ -1168,10 +1166,3 @@ object TestStrategy {
def getIncovations() = invocations
def increment() = invocations += 1
}

object TestEnvFirstStrategy extends EnvFirstConfigLoadingStrategy {
def putEnvVar(key: String, value: String) =
EnvFirstConfigLoadingStrategy.env.put(key, value)
def removeEnvVar(key: String) =
EnvFirstConfigLoadingStrategy.env.remove(key)
}

0 comments on commit 7fe4788

Please sign in to comment.