添加 ConfigFilter 包装类以强制选项范围

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

添加一个新的类,设计用于包装 cfg.ConfigFilter,并考虑到以下用例

  1. 帮助强制一个模块在未先使用 import_opt() 声明跨模块依赖关系的情况下,不能访问另一个模块注册的选项。

  2. 防止私有配置选项对除注册该选项的模块以外的其他模块可见。

问题描述

跨模块选项依赖

在使用全局 cfg.CONF 对象时,一个模块需要其他模块注册的配置选项的存在是很常见的。

例如,如果模块 ‘foo’ 注册 ‘blaa’ 选项,而模块 ‘bar’ 使用 ‘blaa’ 选项,那么 ‘bar’ 可能会这样做

import foo

print(CONF.blaa)

然而,为什么导入 foo 并不明显(是否未使用,可以删除导入),以及 ‘blaa’ 选项来自哪里。

CONF.import_opt() 方法允许显式声明这种依赖关系

CONF.import_opt('blaa', 'foo')
print(CONF.blaa)

然而,import_opt() 有一个弱点 - 如果 ‘bar’ 使用 import 内置函数导入 ‘foo’,并且没有使用 import_opt() 导入 ‘blaa’,那么 ‘blaa’ 仍然可以使用而不会出现问题。 同样,如果一个模块通过 import_opt() 导入了多个选项,一个懒惰的程序员可以只声明对单个选项的依赖关系。

私有配置选项

注册配置选项的库通常不希望库 API 的用户访问这些配置选项。 如果 API 用户访问私有配置选项,当配置选项被重命名时,这些用户将会受到干扰。 换句话说,通常不希望私有配置选项的名称成为公共 API 的一部分。

例如,oslo.messaging 库的用户不应该被允许引用由库注册的配置选项(如 CONF.rpc_backend)。

提议的变更

添加 ConfigFilter 类

ConfigFilter 实现认识到 ConfigOpts 实例有两个部分 - 注册在其上的选项模式集,以及从配置文件和命令行解析的原始配置值。

通过简单地确保 ConfigOpts 拥有自己的选项模式集,但仍然与底层 ConfigOpts 共享解析后的值(_namespace 属性),我们可以允许 ConfigFilter 作为底层解析值的不同视图。

跨模块选项依赖

ConfigFilter 类将提供一种确保选项不可用的方式,除非它们已在模块中注册或使用 import_opt() 导入,例如

CONF = ConfigFilter(cfg.CONF)
CONF.import_opt('blaa', 'foo')
print(CONF.blaa)

除了 ‘blaa’ 之外,没有其他选项可以通过 CONF 访问。

私有配置选项

ConfigFilter 类将提供一种方法,让库可以注册选项,使其对 API 用户提供的 ConfigOpts 实例不可见。 例如

from __future__ import print_function

from oslo.config.cfg import *
from openstack.common.cfgfilter import *

class Widget(object):

    def __init__(self, conf):
        self.conf = conf
        self._private_conf = ConfigFilter(self.conf)
        self._private_conf.register_opt(StrOpt('foo'))

    @property
    def foo(self):
        return self._private_conf.foo

conf = ConfigOpts()
widget = Widget(conf)
print(widget.foo)
print(conf.foo)  # raises NoSuchOptError

备选方案

没有其他方法来解决跨模块依赖问题。

可以通过使配置选项成为公共 API 的一部分,并提供配置选项重命名或移动时的向后兼容性支持来解决私有配置选项问题。 这个选项被拒绝,因为似乎即使提供这种向后兼容性,也不希望无条件地通过 API 暴露配置选项。

Impact on Existing APIs

对现有 API 没有直接影响,但 ConfigFilter 使用 ConfigOpts 的私有实现细节,其中引用 ConfigOpts._namespace 和 ConfigOpts._args 将 ConfigFilter 的 ConfigOpts 实例与包装的 ConfigOpts 实例中的原始解析值关联起来。

安全影响

无。

性能影响

每个 ConfigFilter 实例的最小内存占用,以及最小的查找时间开销。

Configuration Impact

无。

开发人员影响

选择使用 ConfigFilter 的开发人员需要更改配置选项暴露给 Oslo 配置文件的生成方式,因为选项将无法通过 cfg.CONF 发现。 相反,开发人员需要注册一个 oslo.config.opts 入口点,该入口点返回可用配置选项的列表。

该更改会影响 oslo.config 开发人员,使 ConfigOpts 稍后更难重构,因为 ConfigFilter 依赖于其实现细节。

实现

负责人

主要负责人

markmc@redhat.com

里程碑

完成目标里程碑

juno-1

工作项

工作项应该很简单 - 一个可用的 ConfigFilter 实现正在 oslo-incubator 中等待审查。 它只需要移动到 oslo.config 中,无需任何更改。

孵化

cfgfilter 模块存在于 oslo-incubator 中,但目前还没有用户,因此可以将其删除。

采用

oslo.messaging 很可能会最先采用它。

oslo.config。

预计 API 稳定

API 现在应该稳定了。

文档影响

无。

依赖项

无。

参考资料

注意

本作品采用知识共享署名 3.0 非移植许可协议授权。 http://creativecommons.org/licenses/by/3.0/legalcode