mirror of
https://github.com/lightbend/config.git
synced 2025-01-16 07:10:21 +08:00
Add a config strategy to load from environment variables first.
This commit is contained in:
parent
d6023a111d
commit
d9d1f0a3fc
config
checkstyle-config.xmlcheckstyle-suppressions.xml
src
main/java/com/typesafe/config
test/scala/com/typesafe/config/impl
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<module name="Checker">
|
||||
<module name="SuppressionFilter">
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
<suppressions>
|
||||
<!-- don't care about javadoc coverage of methods in impl -->
|
||||
<suppress checks="JavadocMethod"
|
||||
|
@ -0,0 +1,79 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Environment variables first config loading strategy. Able to load environment variables first and fallback to {@link DefaultConfigLoadingStrategy}
|
||||
*
|
||||
* <p>
|
||||
* Environment variables are mangled in the following way after stripping the prefix:
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th bgcolor="silver">Env Var</th>
|
||||
* <th bgcolor="silver">Config</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>_ [1 underscore]</td>
|
||||
* <td>. [dot]</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>__ [2 underscore]</td>
|
||||
* <td>- [dash]</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>___ [3 underscore]</td>
|
||||
* <td>_ [underscore]</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* <p>
|
||||
* A variable like: {@code CONFIG_a_b__c___d}
|
||||
* is translated to a config key: {@code a.b-c_d}
|
||||
*
|
||||
* <p>
|
||||
* The prefix may be altered by defining the VM property {@code config.env_var_prefix}
|
||||
*/
|
||||
public class EnvFirstConfigLoadingStrategy extends DefaultConfigLoadingStrategy {
|
||||
|
||||
protected static Map<String, String> env = new HashMap(System.getenv());
|
||||
|
||||
@Override
|
||||
public Config parseApplicationConfig(ConfigParseOptions parseOptions) {
|
||||
String envVarPrefix = System.getProperty("config.env_var_prefix");
|
||||
if (envVarPrefix == null) // fallback to default
|
||||
envVarPrefix = "CONFIG_";
|
||||
|
||||
Map<String, String> defaultsFromEnv = new HashMap();
|
||||
for (String key : env.keySet()) {
|
||||
if (key.startsWith(envVarPrefix)) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String strippedPrefix = key.substring(envVarPrefix.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();
|
||||
defaultsFromEnv.put(propertyKey, env.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
return ConfigFactory.parseMap(defaultsFromEnv).withFallback(super.parseApplicationConfig(parseOptions));
|
||||
}
|
||||
}
|
@ -1090,6 +1090,53 @@ class ConfigTest extends TestUtils {
|
||||
assertEquals(10, resolved.getInt("bar.nested.a.q"))
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
try {
|
||||
val loader02 = new TestClassLoader(this.getClass().getClassLoader(),
|
||||
Map("reference.conf" -> resourceFile("test02.conf").toURI.toURL()))
|
||||
|
||||
val loader04 = new TestClassLoader(this.getClass().getClassLoader(),
|
||||
Map("reference.conf" -> resourceFile("test04.conf").toURI.toURL()))
|
||||
|
||||
val conf02 = withContextClassLoader(loader02) {
|
||||
ConfigFactory.load()
|
||||
}
|
||||
|
||||
val conf04 = withContextClassLoader(loader04) {
|
||||
ConfigFactory.load()
|
||||
}
|
||||
|
||||
assertEquals(1, conf02.getInt("42_a"))
|
||||
assertEquals(2, conf02.getInt("a.b.c"))
|
||||
assertEquals(3, conf02.getInt("a-c"))
|
||||
assertEquals(4, conf02.getInt("a_c"))
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def renderRoundTrip() {
|
||||
val allBooleans = true :: false :: Nil
|
||||
|
@ -653,6 +653,28 @@ class PublicApiTest extends TestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def supportsEnvFirstConfigLoadingStrategy(): Unit = {
|
||||
assertEquals("config.strategy is not set", null, System.getProperty("config.strategy"))
|
||||
|
||||
TestEnvFirstStrategy.putEnvVar("CONFIG_a", "5")
|
||||
System.setProperty("config.strategy", classOf[EnvFirstConfigLoadingStrategy].getCanonicalName)
|
||||
|
||||
try {
|
||||
val loaderA1 = new TestClassLoader(this.getClass().getClassLoader(),
|
||||
Map("reference.conf" -> resourceFile("a_1.conf").toURI.toURL()))
|
||||
|
||||
val configA1 = withContextClassLoader(loaderA1) {
|
||||
ConfigFactory.load()
|
||||
}
|
||||
|
||||
assertEquals(5, configA1.getInt("a"))
|
||||
} finally {
|
||||
System.clearProperty("config.strategy")
|
||||
TestEnvFirstStrategy.removeEnvVar("CONFIG_a")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def usesContextClassLoaderForApplicationConf() {
|
||||
val loaderA1 = new TestClassLoader(this.getClass().getClassLoader(),
|
||||
@ -1145,4 +1167,11 @@ object TestStrategy {
|
||||
private var invocations = 0
|
||||
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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user