Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hierarchical environment variables #162

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions vertx-config/src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,15 @@ must be listed individually:
{@link examples.ConfigExamples#env3()}
----

Furthermore, there is the `hierarchical` attribute (`false` by default). If `hierarchical` is `true`, the environment variables will be parsed as a nested JSON object, using the dot-separated property name as the path in the JSON object.

Example:

[source, $lang]
----
{@link examples.ConfigExamples#envHierarchical()}
----

=== System Properties

This configuration store transforms system properties to a JSON Object contributed to the
Expand Down
5 changes: 5 additions & 0 deletions vertx-config/src/main/java/examples/ConfigExamples.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ public void env3() {
.setConfig(new JsonObject().put("keys", new JsonArray().add("SERVICE1_HOST").add("SERVICE2_HOST")));
}

public void envHierarchical() {
ConfigStoreOptions envHierarchical = new ConfigStoreOptions()
.setType("env")
.setConfig(new JsonObject().put("hierarchical", true));
}

public void http() {
ConfigStoreOptions http = new ConfigStoreOptions()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
Expand All @@ -44,13 +43,15 @@ public class EnvVariablesConfigStore implements ConfigStore {

private final VertxInternal vertx;
private final boolean rawData;
private final boolean hierarchical;
private final Set<String> keys;
private final Supplier<Map<String, String>> getenv;
private final AtomicReference<Buffer> cached = new AtomicReference<>();

public EnvVariablesConfigStore(Vertx vertx, boolean rawData, JsonArray keys, Supplier<Map<String, String>> getenv) {
public EnvVariablesConfigStore(Vertx vertx, boolean rawData, boolean hierarchical, JsonArray keys, Supplier<Map<String, String>> getenv) {
this.vertx = (VertxInternal) vertx;
this.rawData = rawData;
this.hierarchical = hierarchical;
this.keys = (keys == null) ? null : new HashSet<>(keys.getList());
this.getenv = getenv;
}
Expand All @@ -59,20 +60,19 @@ public EnvVariablesConfigStore(Vertx vertx, boolean rawData, JsonArray keys, Sup
public Future<Buffer> get() {
Buffer value = cached.get();
if (value == null) {
value = all(getenv.get(), rawData, keys).toBuffer();
value = all(getenv.get(), rawData, hierarchical, keys).toBuffer();
cached.set(value);
}
return vertx.getOrCreateContext().succeededFuture(value);
}

private static JsonObject all(Map<String, String> env, boolean rawData, Set<String> keys) {
private static JsonObject all(Map<String, String> env, boolean rawData, boolean hierarchical, Set<String> keys) {
JsonObject json = new JsonObject();
Collection<String> localKeys = keys == null ? env.keySet() : keys;
env.forEach((key, value) -> {
if (localKeys.contains(key)) {
JsonObjectHelper.put(json, key, value, rawData);
for (Map.Entry<String, String> entry : env.entrySet()) {
if (keys == null || keys.contains(entry.getKey())) {
JsonObjectHelper.put(json, entry.getKey(), entry.getValue(), rawData, hierarchical);
}
});
}
return json;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ public String name() {

@Override
public ConfigStore create(Vertx vertx, JsonObject configuration) {
return new EnvVariablesConfigStore(vertx, configuration.getBoolean("raw-data", false), configuration.getJsonArray("keys"), getenv);
return new EnvVariablesConfigStore(vertx, configuration.getBoolean("raw-data", false), configuration.getBoolean("hierarchical", false), configuration.getJsonArray("keys"), getenv);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@ public static void put(JsonObject json, String name, String value, boolean rawDa
json.put(name, rawData ? value : convert(value));
}

public static void put(JsonObject json, String name, String value, boolean rawData, boolean hierarchical) {
if (!hierarchical) {
put(json, name, value, rawData);
} else {
List<String> path = Arrays.asList(name.split("\\."));
if (path.size() == 1) {
put(json, name, value, rawData);
} else {
JsonObject current = json;
for (int i = 0; i < path.size() - 1; i++) {
String key = path.get(i);
if (!current.containsKey(key)) {
current.put(key, new JsonObject());
}
current = current.getJsonObject(key);
}
put(current, path.get(path.size() - 1), value, rawData);
}
}
}

public static Object convert(String value) {
Objects.requireNonNull(value);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public void testLoadingFromEnvUsingRawData() {
retriever = ConfigRetriever.create(vertx,
new ConfigRetrieverOptions()
.addStore(new ConfigStoreOptions().setType("mock-env").setConfig(new JsonObject()
.put("raw-data", true)
.put("env", Collections.singletonMap("name", "12345678901234567890"))
.put("raw-data", true)
.put("env", Collections.singletonMap("mock.name", "12345678901234567890"))
)
)
);
Expand All @@ -53,7 +53,7 @@ public void testLoadingFromEnvUsingRawData() {
retriever.getConfig().onComplete(ar -> {
assertThat(ar.succeeded()).isTrue();
assertThat(ar.result().getString("PATH")).isNotNull();
assertThat(ar.result().getString("name")).isEqualTo("12345678901234567890");
assertThat(ar.result().getString("mock.name")).isEqualTo("12345678901234567890");
done.set(true);
});
await().untilAtomic(done, is(true));
Expand All @@ -63,7 +63,36 @@ public void testLoadingFromEnvUsingRawData() {
public void testLoadingFromEnvWithoutRawData() {
retriever = ConfigRetriever.create(vertx,
new ConfigRetrieverOptions()
.addStore(new ConfigStoreOptions().setType("mock-env").setConfig(new JsonObject().put("env", Collections.singletonMap("name", "12345678901234567890"))))
.addStore(new ConfigStoreOptions().setType("mock-env").setConfig(new JsonObject()
.put("env", Collections.singletonMap("mock.name", "12345678901234567890"))
)
)
);

AtomicBoolean done = new AtomicBoolean();

retriever.getConfig().onComplete(ar -> {
assertThat(ar.succeeded()).isTrue();
try {
// We don't mind the value (truncated, we just want to make sure it doesn't throw an exception)
assertThat(ar.result().getInteger("mock.name")).isNotNull();
} catch (ClassCastException e) {
throw new AssertionError("Should not throw exception", e);
}
done.set(true);
});
await().untilAtomic(done, is(true));
}

@Test
public void testHierarchicalLoadingFromEnv() {
retriever = ConfigRetriever.create(vertx,
new ConfigRetrieverOptions()
.addStore(new ConfigStoreOptions().setType("mock-env").setConfig(new JsonObject()
.put("hierarchical", true)
.put("env", Collections.singletonMap("mock.name", "12345678901234567890"))
)
)
);

AtomicBoolean done = new AtomicBoolean();
Expand All @@ -72,7 +101,8 @@ public void testLoadingFromEnvWithoutRawData() {
assertThat(ar.succeeded()).isTrue();
try {
// We don't mind the value (truncated, we just want to make sure it doesn't throw an exception)
assertThat(ar.result().getInteger("name")).isNotNull();
assertThat(ar.result().getJsonObject("mock")).isNotNull();
assertThat(ar.result().getJsonObject("mock").getInteger("name")).isNotNull();
} catch (ClassCastException e) {
throw new AssertionError("Should not throw exception", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public String name() {
@Override
public ConfigStore create(Vertx vertx, JsonObject configuration) {
JsonObject envConfig = configuration.getJsonObject("env");
return new EnvVariablesConfigStore(vertx, configuration.getBoolean("raw-data", false), configuration.getJsonArray("keys"), () -> {
return new EnvVariablesConfigStore(vertx, configuration.getBoolean("raw-data", false), configuration.getBoolean("hierarchical", false), configuration.getJsonArray("keys"), () -> {
Map<String, String> env = new HashMap<>(System.getenv());
if (envConfig != null) {
for (Map.Entry entry : envConfig) {
Expand Down