Backend Drivers for oslo.config

https://blueprints.launchpad.net/oslo.config/+spec/oslo-config-drivers

目前,oslo.config 与纯文本配置文件紧密绑定。此规范描述了通过向 oslo.config 添加驱动层,允许部署者将配置数据存储在其他位置(例如由 castellan 管理的密钥存储)的变化。

问题描述

各种法规和最佳实践表明,密码和其他机密值不应以纯文本形式存储在配置文件中。存在“密钥存储”服务来管理应保密的这些值。Castellan 提供了一个用于访问这些服务的抽象 API。Castellan 也依赖于 oslo.config,这意味着 oslo.config 无法直接使用 castellan。

此规范描述了 oslo.config 的新驱动 API,以便我们(单独地)编写一个在 castellan 中的驱动程序,该驱动程序在安装时可以使用,但不会在缺失时导致错误。

提议的变更

oslo.config 已更新,以便可以使用多个后端,并将现有的 INI 解析器转换为一个驱动程序。

在初始化 ConfigOpts 实例和加载设置时,将询问所有已知的驱动程序,以确定它们是否可以提供一个或多个数据的“来源”。驱动程序负责定义自己的配置选项并解释它们以定义数据“来源”。

添加了一个新的配置选项 DEFAULT.config_sources,用于定义除 --config-file 和命令行上的 --config-dir 之外的其他来源。 config_sources 的值是源标识符列表,用于查找其他来源的配置设置。每个源标识符对应于一个配置选项组,该组提供单个配置数据源的详细信息。

ConfigOpts 查找选项值时,它会按照提供的顺序遍历定义的来源,从命令行开始,然后是任何配置文件,最后是 config_sources 加载的来源。第一个提供配置选项值的来源将结束搜索(来源不会“合并”在内部)。

--config-dir 的出现将继续被视为单个来源,就像现在一样(所有选项都合并到一个命名空间中),使用 INI 文件驱动程序。

实现细节

ConfigOpts 完成解析命令行选项和加载配置文件后,它将遍历 DEFAULT.config_sources 中的项目。列表值中的每个字符串都将被解释为定义另一个配置来源的选项组的名称。每个组中的 driver 设置将指定要使用的 oslo.config 驱动程序,以使用 open_source_from_opt_group() 从选项组加载来源。

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 实例将保留其维护连接所需的任何信息。连接是长期存在的还是每次需要配置值时打开,由驱动程序决定。

ConfigurationSource 不会被显式关闭。如果连接丢失,驱动程序的责任是重新打开它,或者发出硬性故障异常。

每次 ConfigOpts 被要求提供选项的值时,它将按照注册的顺序遍历驱动程序和来源,并调用 get() 在来源上,始终传递显式的组名和选项名。

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.
    """

如果数据存储区中存在该选项,则来源应返回该选项的值。返回值应与 Opt 的类型匹配,或者另一个可以强制转换为所需类型的类型,例如字符串。在这种情况下,调用者将执行类型转换。

将复杂的类型(如列表和字典)转换为存储格式由驱动程序指定,但如果需要对值进行编码,最好使用 JSON 或 INI 文件使用的语法。

如果数据存储区中不可用该值,则来源应返回 oslo_config.drivers.NoValue。我们不能使用 None 作为指示缺失值的标记,因为它可能是有效值或默认值,因此我们使用自定义单例代替。

提供了 opt 参数,以防驱动程序需要执行高级强制转换(例如将整数值映射到选择)。除非在特殊情况下,否则不应将其用于类型强制转换。

驱动程序不负责处理弃用的选项名称。调用者将使用当前选项名称查找值,然后在没有来源在当前名称下具有值的情况下,再次使用弃用的名称(s)进行搜索。

需要从 ConfigFilesNotFoundErrorConfigFilesPermissionDeniedError 派生新的错误类,以便在驱动程序中类似情况下使用。

从驱动程序引发的所有其他错误都将由 ConfigOpts 捕获并记录为警告,并且驱动程序将被视为返回 NoValue

驱动程序将在 oslo.config 库内实现,并通过 stevedore 管理的入口点加载,使用命名空间 oslo.config.driver。这将允许我们,例如,在 castellan 库中添加一个驱动程序,而不会在 castellan 和 oslo.config 之间引入循环依赖关系。

缓存和可变选项处理

现有的“更改配置”行为,它允许服务告诉 oslo.config 重新加载配置文件,已扩展为适用于新的配置来源。

ConfigurationSource 获取的值可能会被 ConfigOpts 实例缓存,以避免重复调用远程服务(它们不应由驱动程序缓存)。当 ConfigOpts 类被告知更改其选项时,它会丢弃其持有的任何缓存值,以及任何打开的 ConfigurationSource 实例。然后它将从头开始重新加载其配置来源。这避免了在 ConfigurationSource 类中需要缓存刷新 API,从而使驱动程序保持简单。

对于未配置为可变的选项,现有的检测更改行为得以保留,以及用于通知应用程序选项已重新加载的现有回调系统。

备选方案

此规范的早期版本侧重于容器用例的 etcd 驱动程序。该问题已通过不同的方法解决。

其他后端,例如 castellan、consul、zookeeper、MySQL 和 etcd,可以单独实现,无需编写其他规范,除非实现它们需要修改为驱动程序定义的 API。

有一个 另一个提案 引入了配置选项的代理接口。但是,它没有提供任何使其可配置的机制。

Impact on Existing APIs

oslo.config 的现有公共 API 没有更改。

将向 API 添加 ConfigurationSource 类和新的异常。

“更改”配置时 oslo.config 的行为将发生变化,但执行更改的调用保持不变。

安全影响

我们假设任何远程访问都将通过加密连接进行。

性能影响

由于可以在操作期间的任何时间点由服务注册配置选项,因此在流程启动的早期初始化的驱动程序可能无法在一次调用中加载“所有”设置。因此,某些配置访问可能比仅从 INI 文件读取慢。我们可以在 oslo.config 的顶层使用缓存来减轻这种影响。驱动程序可以自由地在其内部实现自己的优化(例如,获取命名空间中的所有键或表中的所有行),但驱动程序 API 中不假定能够这样做。

Configuration Impact

使用密钥存储的部署者需要使用本机工具将配置值加载到其数据库中。每个后端服务的方案必须记录下来,以便他们能够做到这一点。

我们可能希望构建一个工具来读取 INI 文件并将其发布到远程系统,但这不属于此规范的范围,并且在实施之前必须单独描述。部署工具(如 Tripleo)可能会提供其机制来执行此操作,或者通过 oslo.config 贡献工作以进行共享。

下面是一个使用配置文件和通过配置文件设置的假设密钥存储的示例。

程序使用命令行上的标准 --config-file 选项启动。

$ app --config-file /path/to/file.conf

配置文件 file.conf 包含

[DEFAULT]
config_sources = secret

[secret]
driver = castellan
mapping_file = /path/to/mapping.ini

开发人员影响

开发人员不会注意到他们对 oslo.config 的使用有任何差异。

Testing Impact

我们需要对优先级解析算法进行单元测试。

我们需要对驱动程序进行单元测试。

我们需要对驱动程序进行功能测试。

实现

负责人

主要负责人

  • Samuel Pilla (spilla)

其他贡献者

  • Doug Hellmann (dhellmann)

里程碑

queens-3 或 rocky-2

工作项

  • 定义配置驱动程序的基类。

  • 定义 ConfigurationSource 基类。

  • 设置入口点的命名空间以供驱动程序使用。

  • 定义一个用于从简单 URL 加载配置的新驱动程序,用作测试用例。

  • 扩展 ConfigOpts 以加载和使用驱动程序,如上所述。这将添加 URL 处理,而不会更改文件加载的方式。

  • 更新 ConfigOpts 以使用上述 ConfigurationSource 搜索算法,以及其当前的搜索算法。

  • 确保 ConfigOpts 仅缓存非可变值。

  • 更改 mutate_config_files() 以丢弃所有现有数据并重新加载它,而不执行任何验证。修复测试并删除重写该功能后留下的死代码。

孵化

无。

采用

待定

oslo.config

预计 API 稳定

待定

文档影响

待定

依赖项

参考资料

注意

此作品在 知识共享署名 3.0 非本地化许可 下授权。