Backend Drivers for oslo.config¶
Currently, oslo.config is tightly bound to the plain text configuration files. This spec describes changes to allow deployers to store configuration data in other places, such as secret stores managed by castellan, by adding a driver layer to oslo.config.
Various regulations and best practices say that passwords and other secret values should not be stored in plain text in configuration files. There are “secret store” services to manage values that should be kept secure. Castellan provides an abstraction API for accessing those services. Castellan also depends on oslo.config, which means oslo.config cannot use castellan directly.
This spec describes a new driver API for oslo.config to allow us to (separately) write a driver in castellan that can be used when it is installed but will not cause errors when it is missing.
oslo.config is updated so that multiple backends can be used and the existing INI parser is turned into one driver.
As part of initializing the
ConfigOpts instance and loading
settings, all known drivers will be interrogated to determine if they
can provide one or more “sources” of data. The driver is responsible
for defining its own configuration options and interpreting them to
define a data “source”.
A new configuration option
DEFAULT.config_sources is added to
define sources other than those provided by
--config-dir on the command line. The value for
is a list of source identifiers used to find configuration settings
for other sources. Each source identifier corresponds to a
configuration option group, which provides the details for a single
source of configuration data.
ConfigOpts looks for an option value, it goes through the
defined sources in the order they are provided, starting with the
command line, then any configuration files, and finally the sources
config_sources. The first source that provides a
configured value for an option causes the search to end (sources are
not “merged” internally).
An occurance of
--config-dir will continue to be treated as a
single source, as it is now (all of the options are merged into one
namespace) using the INI file driver.
ConfigOpts is done parsing the command line options and
loading configuration files, it will iterate over the items in
DEFAULT.config_sources. Each string in the list value will be
interpreted as the name of an option group that defines another
configuration source. The
driver setting in each group will
oslo.config driver to use to load a source from the
option group using
def open_source_from_opt_group(self, conf, group_name): """Return an open option source. Use group_name to find the configuration settings for the new source then open the source and return it. If a source cannot be open, raise an appropriate exception. :param conf: The active configuration option handler from which to seek configuration values. :type conf: ConfigOpts :param group_name: The configuration option group name where the options for the source are stored. :type group_name: str :return: instance of subclass of ConfigurationSource """
ConfigurationSource instance is expected to retain any
information it needs to maintain a connection. Whether the connection
is long-lived or opened each time a configuration value is needed is
up to the driver.
ConfigurationSource is not explicitly closed. If a connection
is lost, it is the responsibility of the driver to re-open it, or emit
a hard failure exception.
ConfigOpts is asked for the value of an option, it will
iterate over the drivers and sources in the order they were registered
get() on the source, always passing an explicit group
name and option name.
def get(self, group_name, option_name, opt): """Return the value of the option from the group. :param group_name: Name of the group. :type group_name: str :param option_name: Name of the option. :type option_name: str :param opt: The option definition. :type opt: Opt :return: Option value or NoValue. """
The source should return the value of the option, if it is present in
the data store being accessed. The return value should either match
the type for the
Opt, or another type such as a string that can be
coerced into the required type. The caller will perform the type
conversion in that case.
Converting complex types such as lists and dictionaries for storage is left up to the driver to specify, though if the values need to be encoded it would be good if that happened either as JSON or using the same syntax that the INI files use.
If the value is not available in the data store, the source should
oslo_config.drivers.NoValue. We cannot use
None as a
sentinel indicating a missing value because it may be a valid value or
default, so we use a custom singleton instead.
The opt parameter is provided in case the driver needs to do advanced coercion (such as mapping an integer value to a choice). It should not be used for type coercion except in special circumstances.
The driver is not responsible for handling deprecated option names. The caller will look for a value using the current option name then search again using the deprecated name(s) if no source has a value under the current name.
New error classes with more generic names need to be derived from
to be used in similar situations by the drivers.
All other errors raised from the drivers will be trapped by
ConfigOpts and logged as warnings and the driver will be treated
as though it returned
Drivers will be implemented inside the oslo.config library, and loaded
through entry points managed by stevedore using the namespace
oslo.config.driver. This will allow us, for example, to add a
driver to the castellan library without introducing a circular
dependency between castellan and oslo.config.
Caching and Mutable Option Handling¶
The existing “mutate configuration” behavior, which allows a service to tell oslo.config to reload the configuration file, is extended to work with the new configuration sources.
Values retrieved from a
ConfigurationSource may be cached by the
ConfigOpts instance to avoid repeated calls to a remote service (they
should not be cached by the driver). When the
is told to mutate its options, it discards any cached values it holds,
as well as any open
ConfigurationSource instances. It will then
load its configuration sources again, from scratch. This avoids the
need for a cache-flushing API in the
keeping the drivers simple.
The existing behavior for detecting changes to options not configured as mutable is retained, as is the existing callback system for notifying applications that options have been reloaded.
An earlier version of this spec focused on an etcd driver for container use cases. That problem has been solved using a different approach.
Other backends, such as castellan, consul, zookeeper, MySQL, and etcd, can be implemented separately without writing additional specs, unless implementing them will require modifying the API defined for the drivers.
There is another proposal that introduces a proxy interface to configuration options. However, it does not provide any mechanism to make it configurable.
Impact on Existing APIs¶
There are no changes to the existing public API for oslo.config.
ConfigurationSource class and the new exceptions will be added
to the API.
The behavior when oslo.config is told to “mutate” its configuration will change, but the call to perform the mutation is the same.
We assume that any remote access would occur over an encrypted connection.
Because configuration options can be registered by a service at any time during operation, it may not always be possible for a driver initialized early in the process start up to load “all” of the settings in one call. Therefore some configuration accesses may be slower than when reading just from an INI file. We can use caching in the top layer in oslo.config to mitigate this impact. Drivers are free to implement their own optimizations internally (such as fetching all of the keys in a namespace or all of the rows from a table), but the ability to do so is not assumed in the driver API.
Deployers using a secret store will need to load configuration values into their database using a native tool. The scheme for each backend service must be documented in order for them to be able to do this.
We may want to build a tool to read an INI file and publish it to a
remote system, but that is not part of this spec and would have to be
described separately before being implemented. Deployment tools such
as Tripleo may provide their mechanism for doing this, or contribute
to doing the work through oslo.config to be shared. It is expected
that drivers will need a
set() method to support uploading
Below is an example using a configuration file and hypothetical secret store set up via the config file.
The program is started using the standard
--config-file option on
the command line.
$ app --config-file /path/to/file.conf
and the configuration file
[DEFAULT] config_sources = secret [secret] driver = castellan mapping_file = /path/to/mapping.ini
Developers will not notice any difference in their use of oslo.config.
We will need unit tests for the priority resolution algorithm.
We will need unit tests for the driver(s).
We will need functional tests for the driver(s).
Samuel Pilla (spilla)
Doug Hellmann (dhellmann)
queens-3 or rocky-2
Define the base class for a configuration driver.
Set up the namespace for entry points for drivers.
Define a new driver for loading configuration from simple URLs to be used as a test case.
ConfigOptsto load and use the drivers, as described above. This will add URL handling without changing the way file loading works.
ConfigOptsto use the
ConfigurationSourcesearch algorithm described above in addition to its current search algorithm.
ConfigOptsonly caches non-mutable values.
mutate_config_files()to discard all of the existing data and reload it, without performing any validation. Fix tests and erase dead code left by rewriting that feature.
This work is licensed under a Creative Commons Attribution 3.0 Unported License.