-
Notifications
You must be signed in to change notification settings - Fork 26
Configuration
A program's configuration system must be able to present external information to the application to control its run-time behavior. Such information often comes in the form of command-line options, configuration files, environment variables, STDIN, etc.
This page discusses how this information should be formed and provided to the application.
A well-designed configuration system separates the preparation of configuration information from the presentation of that information to the program. Such a design expresses a clear separation of computing responsibilities, and it allows for efficient introspection and debugging of configuration artifacts.
A program's configuration is often better expressed as a data declaration data than a data calculation. This is because a data declaration is typically:
- easier to understand,
- easier to reproduce behavior,
- easier to store, and
- easier to debug.
In the table below, the value of par1
is declared in the first column whereas it is calculated in the second column by a function.
Data declaration | Data calculation |
---|---|
par1 = val1 |
par1 = calc_val1() |
Although calc_val1()
may conveniently prepare the value to be assigned to par1
, the function call inserts a level of indirection between the actual value and the parameter assignment. Such indirection introduces:
- the potential for programming errors,
- possible dependencies on utilities (as required by
calc_val1()
) that may change over time, and - harder to understand configurations.
The decisionengine project thus favors data-declaration techniques over data-calculation ones.
Powerful programming languages have facilities developers rely upon to ensure minimal points of maintenance. In a configuration context, however, those facilities are the very things we argue should not be used (for reasons described above).
It is still possible to configure a program with data declaration while still ensuring single points of maintenance. The Jsonnet language, chosen as decisionengine's configuration language, provides such an approach as long as the following guidelines are heeded.
This section describes some best practices in forming decisionengine configurations. It is expected that these guidelines will change over time based on experience using Jsonnet.
local my_var = 42;
{
par1: my_var
par2: my_var
} |
Equivalent configuration {
par1: 42
par2: 42
} |
local vars = import 'other.libsonnet';
{
par: vars.nested_var
}
Jsonnet allows the ability to provide default values that can be overridden. For example:
local defaults = {
font: "Arial",
color: "black"
};
{
font_style: defaults {
color: "red",
style: "italics"
}
} |
Equivalent configuration {
font_style: {
font: "Arial",
color: "red",
style: "italics"
}
} |
Please consult the Jsonnet documentation to learn more about object composition, especially in for cases where the +
and +:
syntax is necessary.
file_size: 200 * 1024 * 1024 # 200 MiB |
Equivalent configuration {
file_size: 209715200 # 200 MiB
} |
As discussed above, programming facilities that introduce significant computation should be avoided in a configuration environment. Use of the following Jsonnet facilities is thus discouraged:
- functions
- array slices
- conditional expressions
- computed field names
- array and object comprehensions
- Identify all Python-based configurations that can be internally converted to JSON via the Python
json
module- Dump the JSON representation to a file, which will serve as the reference file for that configuration file.
- For any Python-based configuration that cannot be internally converted to JSON:
- Identify the components of the configuration that cannot be converted to JSON, and make them compliant.
- Update the affected Python modules to use the JSON-compliant configuration.
- Dump the JSON-compliant configuration to a file, which will serve as the reference file for that configuration.
With this step, no internal conversion to JSON is allowed. The goal here is to actually migrate from the Python-based configuration to the Jsonnet/JSON based one.
- To perform this migration, a script should read the configuration file and try to evaluate it as a JSON document.
- If the evaluation succeeds, then the document should be renamed with a
.jsonnet
extension - If the evaluation fails, then the document must be adjusted until it is JSON compliant.
- This might be achieved by creating a migration script that can do simple text replacement (e.g.
True -> true
) - Once evaluation as JSON succeeds, the file should be renamed with a
.jsonnet
extension.
- This might be achieved by creating a migration script that can do simple text replacement (e.g.
- Each
.jsonnet
file should be read in and dumped to a JSON test file. - The contents of the test file and corresponding reference file must be identical.