Configuration of applications using files
How are configuration files managed ?
To enable easy re-use of configuration files, they are packaged in the jar file of the carnotzet module. When you start an environment, those files will be mounted into corresponding application containers.
You can define configuration files to be mounted in the containers of any downstream dependency (direct or indirect). This can be used to override/merge the configuration of applications in your environment.
Directory Structure in the carnotzet module
From the root of the resources of the module’s Jar :
- The first directory defines the application in which the files will be mounted (can be itself, or a dependency).
- The second level of directory is “files” by convention (to distinguish with other ways to configure applications such as environment variables)
- Sub-directories in “files” define the target location of the file in the application’s container (think of the “files” folder as the root file “/” inside the container)
This is best explained with an example, let’s imagine we have a module named “my-app” which has it’s own configuration file (expected to be /conf/application.properties in the code of my-app). Now the environment also depends on an nginx instance with custom configuration.
It would look like this in my-app’s module:
src/
└── main/
└── resources/
├── my-app/
│ └── files/ # the content of this folder is mounted at the root of the container
│ └── config/
│ └── application.properties
└── nginx/
└── files/ # the content of this folder is mounted at the root of the container
└── etc/
└── nginx/
└── conf/
└── nginx.conf
Any kind of files can be packaged in the maven jar and mounted in applications like this.
Overriding and merging files
You can override any configuration file from any other Carnotzet module within your own environment.
Each Carnotzet module defines its configuration files inside a src/main/resource/${service_name}/files
folder, where service_name
can be the name of your own service, the name of one or more dependencies, or both.
You can therefore override config files of dependencies by putting a file with the same name and path as the file to be overriden inside the dependencies files
folder.
To merge, the only difference is that you need to suffix the file name to merge with .merge in your own project.
Here’s an example:
in “a-dependency” module
src/
└── main/
└── resources/
└── a-dependency/
└── files/
└── config.properties
└── important.xml
in “my-service” module (which depends on “a-dependency”)
src/
└── main/
└── resources/
├── my-service/
│ └── files/
│ └── haha.xml
└── a-dependency/
└── files/
├── config.properties.merge # Merge
└── important.xml # Override
In the example above, the copy of important.xml
included in the a-dependency Carnotzet module is completely ignored, and is instead completely replaced by the important.xml file supplied by my-service.
When properties files are merged, the resulting file contains ALL of the properties that are defined in at least one file, and will keep only one value for each variable. The final value for each variable is selected using the maven dependency conflict resolution algorithm (see below).
Example:
config.properties config.properties.merge result
================= + ======================= = ======
a.b=ab a.b=123456 a.b=123456
c.d=cd foo=foo c.d=cd
e.f=ef e.f=ef
foo=foo
Merging/Overriding carnotzet.properties
In some cases, you may want to override the cartnozet configuration of a module (docker.image, network.aliases, etc…)
It can be done by creating a file src/main/resources/{a-dependency}/carnotzet.properties(.merge)
in your module. This feature was added in version 1.2.0
Merging other types of files
At the moment, Carnotzet only knows how to merge .properties
files, if you need to merge other file types, you may implement the FileMerger
SPI to add support for your file type.
If you want to support merging JSON files, you need to include the carnotezt-file-merger-json
jar in your classpath :
<dependency>
<groupId>com.github.swissquote</groupId>
<artifactId>carnotzet-file-merger-json</artifactId>
<version>${carnotzet.version}</version>
</dependency>
Override and merging resolution algorithm
Carnotzet resolves file overrides and merges using the standard maven dependency conflict resolution mechanism. This means that when the same file is overridden in multiple modules, the override that applies is always the one that resides in the module whose dependency path to the current module is the shortest.
See this article for more details on the algorithm maven uses.
Example : using an external data image
A good example of this use case is to create a sandbox using an external data image. Let’s take back our example we use to explain dependencies. Let’s say that this time, we don’t want to have a mysql database for “app2”, but we want to share the “postgres” database of “app1”.
+------+
| app2 +
+-+----+
|
|
+-+----+
| app1 +----+
+--+---+ |
| |
| |
+--+----+ | +----------+
| redis | +----+ postgres |
+-------+ +----------+
Fist of all “app2-carnotzet” should have a dependency set to “app1-carnotzet”
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>app1-carnotzet</artifactId>
<version>1</version>
</dependency>
</dependencies>
Then, we override the configuration of the postgres image.
src/
└── main/
└── resources/
└── postgres/
└── carnotzet.properties
Finally, in this “carnotzet.properties”, we override the “docker.image” to force the postres of “app1”.
docker.image=my-registry.my-org/app1/postgres:${app1.version}