OpenStack 项目的依赖管理¶
为什么我们需要一个全局需求列表?¶
在 Havana 发布周期中,我们一直遇到将所有 OpenStack 组件安装到单个环境中的一致性问题。问题在于 requirements.txt 在项目之间的同步是一个最终一致性的问题。有些项目会快速更新,有些则不会。我们从未在软件包的需求中指定相同的版本。
由于 pip 包安装的工作方式,这意味着如果你足够幸运,你最终会得到一个可用的系统。如果你不幸运,你很容易在需求更新时破坏整个 OpenStack。
一个例子是,在 Havana 期间的 DevStack gate 运行过程中,python-keystoneclient 通常会被安装/卸载 6 次。如果 python keystoneclient 的最后一个版本与 OpenStack 的某个部分不兼容,就会发生一个很难诊断的错误。
我们还遇到了项目在没有充分考虑这些库的长期影响的情况下添加 Python 库依赖项的问题。该库是否得到积极维护?该库的许可证是否兼容?该库是否重复了我们已经在需求中拥有的现有库的功能?该库是否与 Python 3 兼容?该库是否已经存在于我们目标 Linux 发行版(Ubuntu / Fedora)中?许多问题的答案是否定的。
全局需求为我们提供了一个可以评估这些事情的单一位置,以便我们可以为 OpenStack 就库的适用性做出全局决策。
自 Havana 以来,我们还观察到由于软件的上游发布(无论大小)与 OpenStack 不兼容而导致了重大的 CI 中断。因此,全局需求还充当了一个控制点,以确定 CI 期间将使用的依赖项的精确版本。
解决方案¶
解决方案的机制相对简单。我们维护一个允许在 OpenStack 项目中使用的所有需求的中央列表 (global-requirements.txt)。这对于 pyproject.toml 文件、需求文件 (requirements.txt、test-requirements.txt、doc/requirements.txt) 以及在 setup.cfg 中定义的 extras 强制执行。这由人工维护,更改通过 CI 进行。
我们还维护一个编译后的列表,其中包含 OpenStack CI 系统中已知可用的软件包的确切版本,包括传递依赖项 (upper-constraints.txt)。这通过一个自动过程维护,该过程计算列表并将其更改提回此存储库。因此,OpenStack 库的新版本不会立即使用:它们必须通过此自动过程才能从(或受到)它们的影响。
每个项目团队还可以选择在 lower-constraints.txt 文件中维护一个用于测试项目所用依赖项的“下限”约束列表。 历史上,这是为所有项目完成的,但现在不再如此。 确保这与项目需求内部一致是各个项目的责任。可以配置每个项目的测试作业来使用该文件进行单元或功能测试。
格式¶
global-requirements.txt 支持 pip 需求文件内容的一个子集。 分发只能按名称引用,不能使用 URL。 选项(例如 -e 或 -f)不能使用。 允许使用环境标记和注释。 仅允许用于排除版本的版本说明符,不允许设置所需的最低版本(所需的最低版本可以选择在 requirements.txt 或 pyproject.toml 文件中为每个项目指定)。 如果需要不同的标记符,则可以多次列出单个分发 - 例如,如果依赖项已放弃对给定 Python 版本的支持。
upper-constraints.txt 是机器生成的,仅包含确切版本的列表,可能每个项目有不同的说明符 - 例如,如果依赖项已放弃对旧 Python 版本的支持。
测试运行的强制执行¶
DevStack¶
DevStack 使用 pip -c 选项将所有库固定到已知良好的版本。 edit-constraints 可用于取消固定单个约束,这用于从 git 安装库。
项目中的强制执行¶
已接受需求合同(如在 projects.txt 中列出)的所有项目预计将运行需求兼容性作业。 此作业可确保项目无法将任何依赖项更改为与 global-requirements.txt 不兼容的版本。 它还确保这些项目无法添加尚未包含在 global-requirements.txt 中的需求。 此 check-requirements 作业应在合并到 openstack/requirements 中的 projects.txt 之前合并到 infra 中。
更新流程¶
更新依赖设置可能是一个两步过程。 如果同时创建这两个补丁,#2 可以使用 Depends-On 将其链接到 #1。
添加新依赖项¶
将依赖项添加到
openstack/requirements中的global-requirements.txt,包括排除版本或为 python 2 或 3 选择不同版本的任何说明。 作为同一评审的一部分,运行以下命令tox -e generate。 确保仅更新或添加与您的添加相关的约束。将依赖项添加到项目树中的
pyproject.toml或适当的需求文件中,并提供最低版本说明符。
删除依赖项¶
从项目树中的需求和约束文件中删除依赖项。 确保检查文档文件夹。
检查其他项目是否使用该依赖项。 如果没有,请更新
openstack/requirements以从global-requirements.txt中删除该项目。
更新依赖项的最低版本¶
检查
openstack/requirements中的upper-constraints.txt文件。 如果那里的版本低于所需版本,请准备一个补丁来更新设置。更新项目树中相关需求文件或
pyproject.toml文件中的最低版本。
排除依赖项的版本¶
我们需要在所有项目中保持一致的排除集,以确保 upper-constraints.txt 版本列表保持可安装状态。
检查
openstack/requirements中的global-requirements.txt。 如果它不排除该版本,请准备一个补丁来更新依赖项的说明符。 如果排除的版本当前正在upper-constraints.txt中使用,则在同一补丁中更新该文件。警告
降低 upper-constraints.txt 中的值可能会导致排除另一个项目所依赖的版本。 在继续之前,请检查这种情况。
更新项目树中的相关需求文件以添加排除项。 没有必要将排除项复制到使用该依赖项的每个项目。
评审指南¶
每个评审人员都应该提出的问题集。 提案者可以通过在他们的更改的提交消息中包含这些问题的答案来使评审更容易。
通用评审标准¶
库版本说明不应包含版本上限
作为一个社区,我们重视对损坏的上游需求的早期反馈,因此除非处理异常不稳定的库,否则应避免版本上限。
如果库异常不稳定,我们还应该考虑是否要用一个是稳定的库来替换它,或者为上游社区做出贡献以帮助稳定它。
库说明不应包含最低版本
各个项目可能希望从依赖项的不同“下限”版本开始,因此我们不在
global-requirements.txt文件中显式跟踪这些版本。提交消息应引用消耗项目
最好,评论还应识别需要新说明的功能或蓝图。 理想情况下,应该已经提出更改,以便可以看到它的使用情况。
denylist 用于处理无法约束的依赖项。 例如,每个项目都有不同发布级别的 linters,并且每次发布都会使项目失败(因为它们添加了规则) - 除非我们协调同时将整个 OpenStack 更新到新版本,否则无法全局约束这些。 仅在未约束的位置使用的依赖项不应排除 - 它们将来可能会被约束,并且今天约束它们不会造成任何危害。 denylist 中的条目应包含解释排除原因的注释。
仅更新
projects.txt的评审应在或与其它评审一起通过工作流批准,以便 OpenStack Proposal Bot 能够尽快为其它项目提供帮助。 对于项目删除或添加,当前 PTL(或如果 PTL 提出了更改,则核心)的 +1 就足够了。由 OpenStack Proposal Bot 提议到
upper-constraints.txt的评审允许由单个核心评审人员批准和通过工作流。
冻结¶
项目需求允许评审流程在冻结期间保持不变。 这是由于提案机器人不会提出对项目 requirements.txt 的更改。 项目负责自己的 requirements.txt 维护。
对于新的需求¶
该库是否得到积极维护?
我们真的希望有一些迹象表明该库是我们可以在发现错误时获得支持的东西,并且我们不必接管并分叉该库。
上游的近期活动和一致的发布模型值得赞赏。
该库的代码质量如何?
在只是告诉每个人从互联网上下载任意的第三方代码之前,提交者有必要深入研究代码,以了解该代码是否足够可靠。 这包括确保上游代码具有一些合理的测试。
该库的许可证是否兼容?
该库应根据 Licensing requirements 描述的许可,并且许可证应在添加依赖项的同一行上用注释描述。 如果您对许可兼容性有疑问,例如,在添加 GPL 测试依赖项时,可以向 Robert Collins (lifeless)、Monty Taylor (mordred) 或 Jim Blair (jeblair) 寻求建议。
该库是否已经打包在我们的目标发行版(最新的 Ubuntu LTS / Debian)中?
通过将某些内容添加到 OpenStack
global-requirements.txt,我们基本上要求 Linux 发行版在 OpenStack 的下一个版本中打包它。 如果他们已经这样做了,那就太好了。 如果没有,我们应该谨慎添加它。 Finding Distro Status该库的功能是否已经由
global-requirements.txt中的其他库覆盖?每个人都有自己喜欢的库,但我们不需要在 OpenStack 中使用三个不同的请求模拟库。
如果这个新需求是关于用更适合我们需求的库替换现有库,那么我们也需要一个在合理的时间内放弃旧库的过渡计划。
该库是否需要 OpenStack 项目或相关的开发或基础设施设置?(答案应该是:当然)哪个?
请提供详细信息,例如 Gerrit 变更请求或 Launchpad bug/blueprint,说明添加此库的必要性。
如果库的发布由 OpenStack 发布流程管理,它是否使用 cycle-with-intermediary 发布类型?
这对于确保更新的发布使用需求更新可用于与其他项目的集成/coninstallability 测试至关重要。
我需要更新其他内容吗?
当添加新库时,需要将初始版本添加到
upper-constraints.txt。 之后,OpenStack Proposal Bot 将提出更新。
查找发行版状态¶
来自 OpenStack 发行版支持策略
OpenStack 将其开发工作重点放在最新的 Ubuntu/Debian LTS 上,但不会引入任何使无法在最新的 RHEL/Centos Stream 上运行的更改。
因此,我们真的需要知道这些平台(以及理想情况下 Gentoo 和 SUSE)上的打包的当前状态。
对于不熟悉 Linux 发行版打包的人,可以使用以下工具搜索软件包
Ubuntu - https://packages.ubuntu.com/
Debian - https://packages.debian.org/index
Fedora - https://packages.fedoraproject.org/
Gentoo - https://packages.gentoo.org/
SUSE - https://build.opensuse.org/project/show/devel:languages:python
对于 upper-constraints.txt 更改¶
如果变更是由 OpenStack CI 机器人提出的,那么如果变更已通过 CI,则只需要一个审查者,他们应该直接 +2 +A,无需深思熟虑。
如果变更不是由 OpenStack CI 机器人提出的,并且仅更改了 upper-constraints.txt 文件中的新库版本条目,那么如果变更通过了测试,则应批准该变更。有关发布流程的更多详细信息,请参阅 openstack/releases 中的 README.rst 文件。
如果变更不是由 OpenStack CI 机器人提出的,并且与发布我们的库无关,也不包含 global-requirements.txt 文件的更改,那么应该拒绝该变更:CI 机器人将自行生成相应的变更。如果在 #openstack-infra 中询问是否需要更快地运行机器人。
否则,该变更可能是重新计算约束的结果,而这些约束是在提出 global-requirements.txt 文件的更改时发生变化的。在这种情况下,忽略对 upper-constraints.txt 文件的更改,并审查变更的 global-requirements.txt 部分。
稳定分支维护¶
上限约束¶
大部分工作由发布项目中的 stable-maint 完成。发布项目确保有效的稳定版本(几乎没有 API 级别的更改,仅修复错误等)。发布后,会请求更新需求中的新版本。为了帮助确保稳定分支不会中断,制定了以下限制。
在稳定分支中,我们通常只更新在 OpenStack 社区内管理的项目的约束。当存在 gate 问题时,会对其他项目进行例外处理。这些更新必须手动提出。
需求团队还会验证新版本的需求更改与稳定分支中的需求(GR 和 UC)是否一致。
全局需求¶
在稳定分支上,这些应该很少见,主要用于屏蔽已知的不良版本,或者在极端情况下,添加允许的软件包的最大版本。我们也在努力删除这些限制。在 第一阶段期间,仅允许提高有效的最小值,并且仅由于安全问题。
新需求¶
在绝大多数情况下,这是不允许的。允许的示例是:依赖项的依赖项存在影响 OpenStack 的问题。它未列在 global-requirements.txt 文件中,但它是必需的。为了阻止受影响的发布,同时仍然能够保持需求同步,我们将库列在 global-requirements.txt 文件中,并更新所有需要它的项目。
工具¶
所有工具都需要安装 openstack_requirements(例如,在 Python 虚拟环境中)。所有工具都有 --help 选项,这是该命令的权威文档。
generate-constraints¶
编译一个约束文件,显示安装所有 global-requirements.txt 文件后产生的版本
generate-constraints -p /usr/bin/python2.7 -p /usr/bin/python3.6 \
-r global-requirements.txt -d denylist.txt --version-map 3.6:3.4 \
--version-map 3.6:3.5 > new-constraints.txt
edit-constraints¶
将约束文件中对软件包的所有引用替换为新的规范。DevStack 使用它来启用通常受约束的库的 git 安装
edit-constraints oslo.db "-e file://opt/stack/oslo.db#egg=oslo.db"
check-requirements¶
使用 requirements-check tox 环境(测试通过未安装的 playbook 的 ansible 运行)在本地运行 requirements-check 任务中的验证检查。
tox -e requirements-check -- /path/to/repo/to/test
Tox & 稳定分支¶
社区依赖 tox 进行测试自动化,但其安装的管理方式取决于所用其他工具的版本。
大多数项目都采用了一种脚本来为开发人员提供在其 tox.ini 文件中调用的 facade。该脚本名为 tox_install.sh,需要安装并管理测试所需的依赖项。
该脚本在使用较新版本的 pip 时存在问题,而较新版本的 pip 在遵守约束文件的情况下,在安装依赖项方面变得更加智能。
我正在我的项目中使用的 tox_install.sh,我应该如何处理它?¶
如果您的项目有一个 tox_install.sh 的副本,您应该删除它。所有对该脚本的引用都应转换为使用适当的上限约束文件,通常在项目的 tox.ini 文件中找到。可以在 这里 找到一个示例。
为什么稳定分支由于 tox_install.sh 的问题而失败?¶
根据项目稳定分支的状态,您可能会注意到以下错误
ERROR: You must give at least one requirement to install (see "pip help
install")
此错误是由稳定分支上使用的较新版本的 pip 引起的,该版本与 tox_install.sh 脚本不兼容。
您可以通过以下两种方式解决此问题。
第一种方法是从稳定分支中完全删除 tox_install.sh,并将分支转换为像 master 一样使用约束。
第二种方法,这可能取决于对稳定分支所做的更改的程度,是修补 tox_install.sh 以使其与较新版本的 pip 兼容。可以在此 补丁 中找到如何执行此操作的示例。