Vert.x Config

Vert.x Config provides a way to configure your Vert.x application. It:

  • offers multiple configuration syntaxes (JSON, properties, Yaml (extension), Hocon (extension)…​

  • offers multiple configuration stores such as files, directories, HTTP, git (extension), Redis (extension), system properties and environment properties.

  • lets you define the processing order and overloading

  • supports runtime reconfiguration

Concepts

The library is structured around:

Config Retriever instantiated and used by the Vert.x application. It configures a set of configuration store Configuration store defines a location from where the configuration data is read and also a format (JSON by default)

The configuration is retrieved as a JSON Object.

Using the Config Retriever

To use the Config Retriever, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'

Once done, you first need to instantiate the ConfigRetriever:

ConfigRetriever retriever = ConfigRetriever.create(vertx);

By default, the Config Retriever is configured with the following stores (in this order):

  • The Vert.x verticle config()

  • The system properties

  • The environment variables

  • A conf/config.json file. This path can be overridden using the vertx-config-path system property or VERTX_CONFIG_PATH environment variable.

You can configure your own stores:

ConfigStoreOptions httpStore = new ConfigStoreOptions()
  .setType("http")
  .setConfig(new JsonObject()
    .put("host", "localhost").put("port", 8080).put("path", "/conf"));

ConfigStoreOptions fileStore = new ConfigStoreOptions()
  .setType("file")
  .setConfig(new JsonObject().put("path", "my-config.json"));

ConfigStoreOptions sysPropsStore = new ConfigStoreOptions().setType("sys");


ConfigRetrieverOptions options = new ConfigRetrieverOptions()
  .addStore(httpStore).addStore(fileStore).addStore(sysPropsStore);

ConfigRetriever retriever = ConfigRetriever.create(vertx, options);

More details about the overloading rules and available stores are available below. Each store can be marked as optional. If a failure is caught while retrieving (or processing) the configuration from an optional store, the failure is logged, but the processing does not fail. Instead, an empty JSON object is returned ({}). To mark a store as optional, use the optional attribute:

ConfigStoreOptions fileStore = new ConfigStoreOptions()
  .setType("file")
  .setOptional(true)
  .setConfig(new JsonObject().put("path", "my-config.json"));
ConfigStoreOptions sysPropsStore = new ConfigStoreOptions().setType("sys");

ConfigRetrieverOptions options = new ConfigRetrieverOptions().addStore(fileStore).addStore(sysPropsStore);

ConfigRetriever retriever = ConfigRetriever.create(vertx, options);

Once you have the instance of the Config Retriever, retrieve the configuration as follows:

retriever.getConfig().onComplete(ar -> {
  if (ar.failed()) {
    // Failed to retrieve the configuration
  } else {
    JsonObject config = ar.result();
  }
});

Overloading rules

The declaration order of the configuration store is important as it defines the overloading. For conflicting key, configuration stores arriving last overloads the value provided by the previous configuration stores. Let’s take an example. We have 2 configuration stores:

  • A provides {a:value, b:1}

  • B provides {a:value2, c:2}

Declared in this order (A, B), the resulting configuration would be: {a:value2, b:1, c:2}.

If you declare them in the reverse order (B, A), you will get: {a:value, b:1, c:2}.

Using the retrieve configuration

The retrieve configuration allows:

  • configuring verticles,

  • configure ports, clients, locations and so on,

  • configuring Vert.x itself

This section gives a few examples of usage.

Configuring a single verticle

The following example can be placed in the start method of a verticle. It retrieves the configuration (using the default stores), and configure an HTTP server with the content of the configuration.

ConfigRetriever retriever = ConfigRetriever.create(vertx);
retriever.getConfig().onComplete(json -> {
  JsonObject result = json.result();

  vertx.createHttpServer()
    .requestHandler(req -> result.getString("message"))
    .listen(result.getInteger("port"));
});

Configuring a set of verticles

The following example configures 2 verticles using the configurations contained in the verticles.json file:

ConfigRetriever retriever = ConfigRetriever.create(vertx, new ConfigRetrieverOptions()
  .addStore(new ConfigStoreOptions().setType("file").setConfig(new JsonObject().put("path", "verticles.json"))));

retriever.getConfig().onComplete(json -> {
  JsonObject a = json.result().getJsonObject("a");
  JsonObject b = json.result().getJsonObject("b");
  vertx.deployVerticle(GreetingVerticle.class.getName(), new DeploymentOptions().setConfig(a));
  vertx.deployVerticle(GreetingVerticle.class.getName(), new DeploymentOptions().setConfig(b));
});

Configuring Vert.x itself

You can also configure Vert.x directly. For this, you need a temporary Vert.x instance used to retrieve the configuration. Then the actual instance is created:

Vertx vertx = Vertx.vertx();
// Create the config retriever
ConfigRetriever retriever = ConfigRetriever.create(vertx, new ConfigRetrieverOptions()
  .addStore(new ConfigStoreOptions().setType("file").setConfig(new JsonObject().put("path", "vertx.json"))));

// Retrieve the configuration
retriever.getConfig().onComplete(json -> {
  JsonObject result = json.result();
  // Close the vert.x instance, we don't need it anymore.
  vertx.close();

  // Create a new Vert.x instance using the retrieve configuration
  VertxOptions options = new VertxOptions(result);
  Vertx newVertx = Vertx.vertx(options);

  // Deploy your verticle
  newVertx.deployVerticle(GreetingVerticle.class.getName(), new DeploymentOptions().setConfig(result.getJsonObject("a")));
});

Propagating configuration changes to the event bus

Vert.x Config notifies you when the configuration changes. If you want to react to this event, you need to implement the reaction yourself. For instance, you can un-deploy and redeploy verticle or send the new configuration on the event bus. The following example shows this latter case. It sends the new configuration on the event bus. Interested verticles can listen for this address and update themselves:

ConfigRetriever retriever = ConfigRetriever.create(vertx, new ConfigRetrieverOptions()
  .addStore(new ConfigStoreOptions().setType("file").setConfig(new JsonObject().put("path", "verticles.json"))));

retriever.getConfig().onComplete(json -> {
  //...
});

retriever.listen(change -> {
  JsonObject json = change.getNewConfiguration();
  vertx.eventBus().publish("new-configuration", json);
});

Available configuration stores

The Config Retriever provides a set of configuration stores and formats. More are available as extensions, and you can also implement your own.

Structure of the configuration

Each declared data store must specify the type. It can also define the format. If not set JSON is used.

Some configurations tore requires additional configuration (such a path…​). This configuration is passed as a Json Object using setConfig

File

This configuration store just read the configuration from a file. It supports all supported formats.

ConfigStoreOptions file = new ConfigStoreOptions()
  .setType("file")
  .setFormat("properties")
  .setConfig(new JsonObject().put("path", "path-to-file.properties"));

The path configuration is required.

JSON

The JSON configuration store serves the given JSON config as it is.

ConfigStoreOptions json = new ConfigStoreOptions()
  .setType("json")
  .setConfig(new JsonObject().put("key", "value"));

The only supported format for this configuration store is JSON.

Environment Variables

This configuration store transforms environment variables to a JSON Object contributed to the global configuration.

ConfigStoreOptions env = new ConfigStoreOptions()
  .setType("env");

This configuration store does not support the format configuration. By default, the retrieved value is transformed into JSON compatible structures (number, string, boolean, JSON object and JSON array). To avoid this conversion, configure the raw-data attribute:

ConfigStoreOptions env = new ConfigStoreOptions()
  .setType("env")
  .setConfig(new JsonObject().put("raw-data", true));

You can configure the raw-data attribute (false by default). If raw-data is true no attempts to convert values are made, and you’ll be able to get raw values using config.getString(key). It is useful when manipulating large integers.

If you want to select the set of keys to import, use the keys attributes. It filters out all not selected keys. Keys must be listed individually:

ConfigStoreOptions env = new ConfigStoreOptions()
  .setType("env")
  .setConfig(new JsonObject().put("keys", new JsonArray().add("SERVICE1_HOST").add("SERVICE2_HOST")));

System Properties

This configuration store transforms system properties to a JSON Object contributed to the global configuration.

ConfigStoreOptions sys = new ConfigStoreOptions()
  .setType("sys")
  .setConfig(new JsonObject().put("cache", false));

This configuration store does not support the format configuration.

You can configure the cache attribute (true by default) let you decide whether or not it caches the system properties on the first access and does not reload them.

You can also configure the raw-data attribute (false by default). If raw-data is true no attempts to convert values are made, and you’ll be able to get raw values using config.getString(key). It is useful when manipulating large integers.

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

Example:

ConfigStoreOptions sysHierarchical = new ConfigStoreOptions()
  .setType("sys")
  .setConfig(new JsonObject().put("hierarchical", true));
java -Dserver.host=localhost -Dserver.port=8080 -jar your-application.jar

This will read the system properties as JSON object equivalent to

{
 "server": {
   "host": "localhost",
   "port": 8080
 }
}

HTTP

This configuration store retrieves the configuration from an HTTP location. It can use any supported format.

ConfigStoreOptions http = new ConfigStoreOptions()
  .setType("http")
  .setConfig(new JsonObject()
    .put("host", "localhost")
    .put("port", 8080)
    .put("path", "/A"));

It creates a Vert.x HTTP Client with the store configuration (see next snippet). To ease the configuration; you can also configure the host, port and path with the host, port and path properties. You can also configure optional HTTP request headers with headers property, the timeout (in milliseconds, 3000 by default) to retrieve the configuration with timeout property, if following redirects (false by default) with followRedirects property.

ConfigStoreOptions http = new ConfigStoreOptions()
  .setType("http")
  .setConfig(new JsonObject()
    .put("defaultHost", "localhost")
    .put("defaultPort", 8080)
    .put("ssl", true)
    .put("path", "/A")
    .put("headers", new JsonObject().put("Accept", "application/json")));

Event Bus

This event bus configuration store receives the configuration from the event bus. This stores let you distribute your configuration among your local and distributed components.

ConfigStoreOptions eb = new ConfigStoreOptions()
  .setType("event-bus")
  .setConfig(new JsonObject()
    .put("address", "address-getting-the-conf")
  );

This configuration store supports any format.

Directory

This configuration store is similar to the file configuration store, but instead of reading a single file, read several files from a directory.

This configuration store configuration requires:

  • a path - the root directory in which files are located

  • at least one fileset - an object to select the files

  • for properties file, you can indicate if you want to disable the type conversion using the raw-data attribute

Each fileset contains:

  • a pattern : an Ant-style pattern to select files. The pattern is applied to the relative path of the files from the current working directory.

  • an optional format indicating the format of the files (each fileset can use a different format, BUT files in a fileset must share the same format).

ConfigStoreOptions dir = new ConfigStoreOptions()
  .setType("directory")
  .setConfig(new JsonObject().put("path", "config")
    .put("filesets", new JsonArray()
      .add(new JsonObject().put("pattern", "dir/*json"))
      .add(new JsonObject().put("pattern", "dir/*.properties")
        .put("format", "properties"))
    ));

ConfigStoreOptions dirWithRawData = new ConfigStoreOptions()
  .setType("directory")
  .setConfig(new JsonObject().put("path", "config")
    .put("filesets", new JsonArray()
      .add(new JsonObject().put("pattern", "dir/*json"))
      .add(new JsonObject().put("pattern", "dir/*.properties")
        .put("format", "properties").put("raw-data", true))
    ));

Properties file and raw data

Vert.x Config can read a properties file. When reading such a file, you can pass the raw-data attribute to indicate to Vert.x to not attempt to convert values. It is useful when manipulating large integers. Values can be retrieved using config.getString(key).

ConfigStoreOptions propertyWithRawData = new ConfigStoreOptions()
  .setFormat("properties")
  .setType("file")
  .setConfig(new JsonObject().put("path", "raw.properties").put("raw-data", true)
  );

Some properties configuration maybe is hierarchical in nature. When reading such a file, you can pass the hierarchical attribute to indicate to Vert.x to convert the configuration to a json object while maintaining this hierarchy, in contrast to the previous method with a flat structure.

Example:

server.host=localhost
server.port=8080
multiple.values=1,2,3

Get values:

ConfigStoreOptions propertyWithHierarchical = new ConfigStoreOptions()
  .setFormat("properties")
  .setType("file")
  .setConfig(new JsonObject().put("path", "hierarchical.properties").put("hierarchical", true)
  );
ConfigRetrieverOptions options = new ConfigRetrieverOptions()
  .addStore(propertyWithHierarchical);

ConfigRetriever configRetriever = ConfigRetriever.create(Vertx.vertx(), options);

configRetriever.configStream().handler(config -> {
  String host = config.getJsonObject("server").getString("host");
  Integer port = config.getJsonObject("server").getInteger("port");
  JsonArray multiple = config.getJsonObject("multiple").getJsonArray("values");
  for (int i = 0; i < multiple.size(); i++) {
    Integer value = multiple.getInteger(i);
  }
});

Listening for configuration changes

The Configuration Retriever periodically retrieves the configuration, and if the outcome is different from the current one, your application can be reconfigured. By default, the configuration is reloaded every 5 seconds.

ConfigRetrieverOptions options = new ConfigRetrieverOptions()
  .setScanPeriod(2000)
  .addStore(store1)
  .addStore(store2);

ConfigRetriever retriever = ConfigRetriever.create(Vertx.vertx(), options);
retriever.getConfig().onComplete(json -> {
  // Initial retrieval of the configuration
});

retriever.listen(change -> {
  // Previous configuration
  JsonObject previous = change.getPreviousConfiguration();
  // New configuration
  JsonObject conf = change.getNewConfiguration();
});

Retrieving the last retrieved configuration

You can retrieve the last retrieved configuration without "waiting" to be retrieved using:

JsonObject last = retriever.getCachedConfig();

Reading configuration as a stream

The ConfigRetriever provide a way to access the stream of configuration. It’s a ReadStream of JsonObject. By registering the right set of handlers you are notified:

  • when a new configuration is retrieved

  • when an error occur while retrieving a configuration

  • when the configuration retriever is closed (the endHandler is called).

ConfigRetrieverOptions options = new ConfigRetrieverOptions()
  .setScanPeriod(2000)
  .addStore(store1)
  .addStore(store2);

ConfigRetriever retriever = ConfigRetriever.create(Vertx.vertx(), options);
retriever.configStream()
  .endHandler(v -> {
    // retriever closed
  })
  .exceptionHandler(t -> {
    // an error has been caught while retrieving the configuration
  })
  .handler(conf -> {
    // the configuration
  });

Processing the configuration

You can configure a processor that can validate and update the configuration. This is done using the setConfigurationProcessor method.

The processing must not return null. It takes the retrieved configuration and returns the processed one. If the processor does not update the configuration, it must return the input configuration. If the processor can throw an exception (for example for validation purpose).

Retrieving the configuration as a Future

The ConfigRetriever provide a way to retrieve the configuration as a Future:

Future<JsonObject> future = retriever.getConfig();
future.onComplete(ar -> {
  if (ar.failed()) {
    // Failed to retrieve the configuration
  } else {
    JsonObject config = ar.result();
  }
});

Extending the Config Retriever

You can extend the configuration by implementing:

  • the ConfigProcessor SPI to add support for a format

  • the ConfigStoreFactory SPI to add support for configuration store (place from where the configuration data is retrieved)

Additional formats

Besides the out of the box format supported by this library, Vert.x Config provides additional formats you can use in your application.

Hocon Configuration Format

The Hocon Configuration Format extends the Vert.x Configuration Retriever and provides the support for the HOCON format.

It supports includes, json, properties, macros…​

Using the Hocon Configuration Format

To use the Hocon Configuration Format, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config-hocon</artifactId>
 <version>4.4.9</version>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'
compile 'io.vertx:vertx-config-hocon:4.4.9'

Configuring the store to use HOCON

Once added to your classpath or dependencies, you need to configure the ConfigRetriever to use this format:

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("file")
  .setFormat("hocon")
  .setConfig(new JsonObject()
    .put("path", "my-config.conf")
  );

ConfigRetriever retriever = ConfigRetriever.create(vertx,
    new ConfigRetrieverOptions().addStore(store));

You just need to set format to hocon.

Override configuration using system environment variables

Hocon supports system environment variable overrides using keys with CONFIG_FORCE_ prefix. You can use this feature by specifying hocon.env.override to true in the configuration:

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("file")
  .setFormat("hocon")
  .setConfig(new JsonObject()
    .put("hocon.env.override", true)
    .put("path", "my-config.conf")
  );

ConfigRetriever retriever = ConfigRetriever.create(vertx,
  new ConfigRetrieverOptions().addStore(store));

Yaml Configuration Format

The Yaml Configuration Format extends the Vert.x Configuration Retriever and provides the support for the Yaml Configuration Format format.

Using the Yaml Configuration Format

To use the Yaml Configuration Format, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config-yaml</artifactId>
 <version>4.4.9</version>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'
compile 'io.vertx:vertx-config-yaml:4.4.9'

Configuring the store to use YAML

Once added to your classpath or dependencies, you need to configure the ConfigRetriever to use this format:

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("file")
  .setFormat("yaml")
  .setConfig(new JsonObject()
    .put("path", "my-config.yaml")
  );

ConfigRetriever retriever = ConfigRetriever.create(vertx,
    new ConfigRetrieverOptions().addStore(store));

You just need to set format to yaml.

Additional stores

Besides the out of the box stores supported by this library, Vert.x Config provides additional stores you can use in your application.

Git Configuration Store

The Git Configuration Store is an extension to the Vert.x Configuration Retriever to retrieve configuration from a Git repository.

Using the Git Configuration Store

To use the Git Configuration, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config-git</artifactId>
 <version>4.4.9</version>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'
compile 'io.vertx:vertx-config-git:4.4.9'

Configuring the store

Once added to your classpath or dependencies, you need to configure the ConfigRetriever to use this store:

ConfigStoreOptions git = new ConfigStoreOptions()
    .setType("git")
    .setConfig(new JsonObject()
        .put("url", "https://github.com/cescoffier/vertx-config-test.git")
        .put("path", "local")
        .put("filesets",
            new JsonArray().add(new JsonObject().put("pattern", "*.json"))));

ConfigRetriever retriever = ConfigRetriever.create(vertx,
    new ConfigRetrieverOptions().addStore(git));

The configuration requires:

  • the url of the repository

  • the path where the repository is cloned (local directory)

  • the user for private repository (no authentication by default)

  • the password of the user

  • the idRsaKeyPath for private repository and requires ssh uri

  • at least fileset indicating the set of files to read (same behavior as the directory configuration store).

You can also configure the branch (master by default) to use and the name of the remote repository (origin by default).

How does it works

If the local path does not exist, the configuration store clones the repository into this directory. Then it reads the file matching the different file sets.

It the local path exist, it tried to update it (it switches branch if needed)). If the update failed the configuration retrieval fails.

Periodically, the repositories is updated to check if the configuration has been updated.

Kubernetes ConfigMap Store

The Kubernetes ConfigMap Store extends the Vert.x Configuration Retriever and provides support Kubernetes Config Map and Secrets. So, configuration is retrieved by reading the config map or the secrets.

Using the Kubernetes ConfigMap Store

To use the Kubernetes ConfigMap Store, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config-kubernetes-configmap</artifactId>
 <version>4.4.9</version>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'
compile 'io.vertx:vertx-config-kubernetes-configmap:4.4.9'

Configuring the store

Once added to your classpath or dependencies, you need to configure the ConfigRetriever to use this store:

ConfigStoreOptions store = new ConfigStoreOptions()
    .setType("configmap")
    .setConfig(new JsonObject()
        .put("namespace", "my-project-namespace")
        .put("name", "configmap-name")
    );

ConfigRetriever retriever = ConfigRetriever.create(vertx,
    new ConfigRetrieverOptions().addStore(store));

You need to configure the store to find the right configmap. this is done with:

  • namespace - the project namespace, default by default. If the KUBERNETES_NAMESPACE ENV variable is set, it uses this value.

  • name - the name of the config map

  • optional - whether or not the config map is optional (true by default)

If the config map is composed by several element, you can use the key parameter to tell which key is read

The application must have the permissions to read the config map.

To read data from a secret, just configure the secret property to true:

ConfigStoreOptions store = new ConfigStoreOptions()
    .setType("configmap")
    .setConfig(new JsonObject()
        .put("namespace", "my-project-namespace")
        .put("name", "my-secret")
        .put("secret", true)
    );

ConfigRetriever retriever = ConfigRetriever.create(vertx,
    new ConfigRetrieverOptions().addStore(store));

If the config map is not available, an empty JSON object is passed as configuration chunk. To disable this behavior and explicitly fail, you can set the optional configuration to false.

Redis Configuration Store

The Redis Configuration Store extends the Vert.x Configuration Retriever and provides the way to retrieve configuration from a Redis server.

Using the Redis Configuration Store

To use the Redis Configuration Store, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config-redis</artifactId>
 <version>4.4.9</version>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'
compile 'io.vertx:vertx-config-redis:4.4.9'

Configuring the store

Once added to your classpath or dependencies, you need to configure the ConfigRetriever to use this store:

ConfigStoreOptions store = new ConfigStoreOptions()
    .setType("redis")
    .setConfig(new JsonObject()
        .put("host", "localhost")
        .put("port", 6379)
        .put("key", "my-configuration")
    );

ConfigRetriever retriever = ConfigRetriever.create(vertx,
    new ConfigRetrieverOptions().addStore(store));

The store configuration is used to create an instance of Redis. check the documentation of the Vert.x Redis Client for further details.

In addition, you can set the key instructing the store in which field the configuration is stored. configuration is used by default.

The created Redis client retrieves the configuration using the HGETALL configuration.

Zookeeper Configuration Store

The Zookeeper Configuration Store extends the Vert.x Configuration Retriever and provides the way to retrieve configuration from a Zookeeper server. It uses Apache Curator as client.

Using the Zookeeper Configuration Store

To use the Redis Configuration Store, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config-zookeeper</artifactId>
 <version>4.4.9</version>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'
compile 'io.vertx:vertx-config-zookeeper:4.4.9'

Configuring the store

Once added to your classpath or dependencies, you need to configure the ConfigRetriever to use this store:

ConfigStoreOptions store = new ConfigStoreOptions()
    .setType("zookeeper")
    .setConfig(new JsonObject()
        .put("connection", "localhost:2181")
        .put("path", "/path/to/my/conf")
    );

ConfigRetriever retriever = ConfigRetriever.create(vertx,
    new ConfigRetrieverOptions().addStore(store));

The store configuration is used to configure the Apache Curator client and the path of the Zookeeper node containing the configuration. Notice that the format of the configuration can be JSON, or any supported format.

The configuration requires the connection attribute indicating the connection string of the Zookeeper server, and the path attribute indicating the path of the node containing the configuration.

In addition you can configure:

  • maxRetries: the number of connection attempt, 3 by default

  • baseSleepTimeBetweenRetries: the amount of milliseconds to wait between retries (exponential backoff strategy). 1000 ms by default.

Consul Configuration Store

The Consul Configuration Store extends the Vert.x Configuration Retriever and provides the way to retrieve configuration from a Consul.

Using the Consul Configuration Store

To use the Consul Configuration Store, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config-consul</artifactId>
 <version>4.4.9</version>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'
compile 'io.vertx:vertx-config-consul:4.4.9'

Configuring the store

Once added to your classpath or dependencies, you need to configure the ConfigRetriever to use this store:

ConfigStoreOptions store = new ConfigStoreOptions()
    .setType("consul")
    .setConfig(new JsonObject()
      .put("host", "localhost")
      .put("port", 8500)
      .put("prefix", "foo")
      .put("raw-data", false)
    );

ConfigRetriever retriever = ConfigRetriever.create(vertx,
    new ConfigRetrieverOptions().addStore(store));

The store configuration is used to create an instance of ConsulClient. Check the documentation of the Vert.x Consul Client for further details. And this is the parameters specific to the Consul Configuration Store:

prefix

A prefix that will not be taken into account when building the configuration tree. Defaults to empty.

delimiter

Symbol that used to split keys in the Consul storage to obtain levels in the configuration tree. Defaults to "/".

raw-data

If raw-data is true no attempts to convert values is made, and you’ll be able to get raw values using config.getString(key). Defaults to true.

Spring Config Server Store

The Spring Config Server Store extends the Vert.x Configuration Retriever and provides the a way to retrieve configuration from a Spring Server.

Using the Spring Config Server Store

To use the Spring Config Server Store, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config-spring-config-server</artifactId>
 <version>4.4.9</version>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'
compile 'io.vertx:vertx-config-spring-config-server:4.4.9'

Configuring the store

Once added to your classpath or dependencies, you need to configure the ConfigRetriever to use this store:

ConfigStoreOptions store = new ConfigStoreOptions()
    .setType("spring-config-server")
    .setConfig(new JsonObject().put("url", "http://localhost:8888/foo/development"));

ConfigRetriever retriever = ConfigRetriever.create(vertx,
    new ConfigRetrieverOptions().addStore(store));

Configurable attributes are:

  • url - the url to retrieve the configuration (mandatory), supports two type of formats:

    • /{application}/{environment} which produces response with separated propertySources

    • /{application}-{environment}.json which produces response as JSON with unique fields and resolved Spring placeholders

  • timeout - the timeout (in milliseconds) to retrieve the configuration, 3000 by default

  • user - the user (no authentication by default)

  • password - the password

  • httpClientConfiguration - the configuration of the underlying HTTP client

Vault Config Store

The Vault Store extends the Vert.x Configuration Retriever and provides support for Vault (https://www.vaultproject.io/). So, configuration (secrets) is retrieved from Vault.

The secrets engines supported by this store are Vault Key/Value version 1 and version 2 engines (https://www.vaultproject.io/docs/secrets/kv/index.html). Other secrets engine are not supported.

Using the Vault Config Store

To use the Vault Config Store, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config-vault</artifactId>
 <version>4.4.9</version>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-config</artifactId>
 <version>4.4.9</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-config:4.4.9'
compile 'io.vertx:vertx-config-vault:4.4.9'

Configuring the store

Once added to your classpath or dependencies, you need to configure the ConfigRetriever to use this store:

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("vault")
  .setConfig(config);

ConfigRetriever retriever = ConfigRetriever.create(vertx,
  new ConfigRetrieverOptions().addStore(store));

To use the Vault config store, set the type to vault. The configuration is provided as Json. It configures the access to Vault, authentication and the path of the secret to retrieve:

JsonObject vault_config = new JsonObject()
  .put("host", "127.0.0.1") // The host name
  .put("port", 8200) // The port
  .put("ssl", true); // Whether or not SSL is used (disabled by default)

// Certificates
PemKeyCertOptions certs = new PemKeyCertOptions()
  .addCertPath("target/vault/config/ssl/client-cert.pem")
  .addKeyPath("target/vault/config/ssl/client-privatekey.pem");
vault_config.put("pemKeyCertOptions", certs.toJson());

// Truststore
JksOptions jks = new JksOptions()
  .setPath("target/vault/config/ssl/truststore.jks");
vault_config.put("trustStoreOptions", jks.toJson());

// Path to the secret to read.
vault_config.put("path", "secret/my-secret");

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("vault")
  .setConfig(vault_config);

ConfigRetriever retriever = ConfigRetriever.create(vertx,
  new ConfigRetrieverOptions().addStore(store));}

The vault_config object can contain the HTTP client / Web client configuration such as trust stores, timeout, certificates, port and host. The path and host entries are mandatory. The path indicates the secret to retrieve. The host is the hostname of the Vault server. By default the port 8200 is used. SSL is disabled by default, but you should enable it for production settings.

Then, you need to use one of the following method to configure the token to use or the authentication mechanism.

Using an existing token

If you know the token to use, set the token entry in the configuration:

JsonObject vault_config = new JsonObject();

// ...

// Path to the secret to read.
vault_config.put("path", "secret/my-secret");

// The token
vault_config.put("token", token);

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("vault")
  .setConfig(vault_config);

ConfigRetriever retriever = ConfigRetriever.create(vertx,
  new ConfigRetrieverOptions().addStore(store));

You can use the root token, but it’s not recommended. When the token is revoked, the access to the secret is blocked. If the token is renewable, the token is renewed when it expires.

Generating a token

If you have a token allowing you to generate new token, you can request the token generation:

JsonObject vault_config = new JsonObject();

// ...

// Path to the secret to read.
vault_config.put("path", "secret/my-secret");

// Configure the token generation

// Configure the token request (https://www.vaultproject.io/docs/auth/token.html)
JsonObject tokenRequest = new JsonObject()
  .put("ttl", "1h")
  .put("noDefault", true)

  // The token to use to request the generation (parts of the tokenRequest object)
  .put("token", token);

vault_config.put("auth-backend", "token") // Indicate the auth backend to use
  .put("renew-window", 5000L) // Renew error margin in ms
  .put("token-request", tokenRequest); // Pass the token generation configuration

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("vault")
  .setConfig(vault_config);

ConfigRetriever retriever = ConfigRetriever.create(vertx,
  new ConfigRetrieverOptions().addStore(store));

When using this approach, no token must be provided in the root configuration, the the token use to request the generation is passed in the nested JSON structure. If the generated token is renewable, it will be renewed automatically upon expiration. The renew-window is the time window to add to the token validity to renew it. If the generated token is revoked, the access to the secret is blocked.

Using certificates

You can use TLS certificates as authentication mechanism. So, you don’t need to know a token, the token is generated automatically.

JsonObject vault_config = new JsonObject();

// ...

PemKeyCertOptions certs = new PemKeyCertOptions()
  .addCertPath("target/vault/config/ssl/client-cert.pem")
  .addKeyPath("target/vault/config/ssl/client-privatekey.pem");
vault_config.put("pemKeyCertOptions", certs.toJson());

PemTrustOptions trust = new PemTrustOptions()
  .addCertPath("target/vault/config/ssl/cert.pem");
vault_config.put("pemTrustStoreOptions", trust.toJson());

JksOptions jks = new JksOptions()
  .setPath("target/vault/config/ssl/truststore.jks");
vault_config.put("trustStoreOptions", jks.toJson());

vault_config.put("auth-backend", "cert");

// Path to the secret to read.
vault_config.put("path", "secret/my-secret");

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("vault")
  .setConfig(vault_config);

ConfigRetriever retriever = ConfigRetriever.create(vertx,
  new ConfigRetrieverOptions().addStore(store));

Check out the HTTP client and Web client configuration to pass the certificates. If the generated token is renewable, it will be renewed. If not, the store attempts to authenticate again.

Using AppRole

AppRole is used when your application is known by Vault and you have the appRoleId and secretId. You don’t need a token, the token being generated automatically:

JsonObject vault_config = new JsonObject();

// ...

vault_config
  .put("auth-backend", "approle") // Set the auth-backend to approle
  .put("approle", new JsonObject()  // Configure the role id and secret it
    .put("role-id", appRoleId).put("secret-id", secretId)
  );

// Path to the secret to read.
vault_config.put("path", "secret/my-secret");

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("vault")
  .setConfig(vault_config);

ConfigRetriever retriever = ConfigRetriever.create(vertx,
  new ConfigRetrieverOptions().addStore(store));

If the generated token is renewable, it will be renewed. If not, the store attempts to authenticate again.

Using username and password

The userpass auth backend is used when the user / app is authenticated using a username/password. You don’t need a token as the token is generated during the authentication process:

JsonObject vault_config = new JsonObject();

// ...

vault_config
  .put("auth-backend", "userpass") // Set the auth-backend to userpass
  .put("user-credentials", new JsonObject()
    .put("username", username).put("password", password)
  );

// Path to the secret to read.
vault_config.put("path", "secret/my-secret");

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("vault")
  .setConfig(vault_config);

ConfigRetriever retriever = ConfigRetriever.create(vertx,
  new ConfigRetrieverOptions().addStore(store));

If the generated token is renewable, it will be renewed. If not, the store attempts to authenticate again.