贡献者指南

本指南旨在帮助任何希望为 community.hashi_vault 集合做出贡献的人。

注意

本指南可以在您的帮助下得到改进!在 存储库中打开一个 GitHub 问题或按照以下说明直接贡献。

快速开始

  1. 登录您的 GitHub 帐户。

  2. 单击右上角的 Fork 按钮,Fork ansible-collections/community.hashi_vault 存储库。这将在您自己的帐户中创建一个 Fork。

  3. 按照此处的示例说明在本地克隆存储库(但将 general 替换为 hashi_vault)。请特别注意这些说明中描述的克隆存储库的本地路径结构(例如 ansible_collections/community/hashi_vault)。

  4. 如该页面所述,将您的更改提交到一个分支,将它们推送到您的 Fork,然后创建一个拉取请求(当您查看您的存储库时,GitHub 将自动提示您这样做)。

  5. 请参阅有关变更日志的指导,并在适当情况下包含一个变更日志片段

贡献文档

非常欢迎添加集合文档!我们有三种主要类型的文档,每种都有自己的语法和规则。

README 和其他 Markdown 文件

Markdown 文件(扩展名为 .md 的文件)可以在存储库中的多个目录中找到。这些文件主要面向开发人员和浏览存储库的人,用于解释或提供附近其他文件的上下文。

上述主要例外情况是存储库根目录中的 README.md。此文件更为重要,因为它为在 GitHub 和集合的 Ansible Galaxy 页面上浏览存储库的任何人提供介绍性信息和链接。

Markdown 文件可以在 GitHub 中原生预览,因此审阅者很容易验证它们,并且不需要针对它们运行特定的测试。

您的 IDE 或代码编辑器也可能能够预览这些文件。例如,Visual Studio Code 具有内置的 markdown 预览功能

模块和插件文档

这种类型的文档是从 Python 字符串内的结构化 YAML 生成的。它包含在它所记录的同一代码中,或在单独的 Python 文件(例如 doc 片段)中。请参阅模块格式和文档指南以获取更多信息。

这种类型的文档是高度结构化的,并使用 ansible-test sanity 进行测试。有关完整说明,请参阅测试模块文档页面。

此外,拉取请求(或在本地构建)上的文档站点构建也将包含模块和插件文档。有关详细信息,请参阅下一节。

集合文档站点

您现在正在阅读的是集合文档站点。它以 reStructuredText (RST) 格式编写,并在Ansible 文档站点上发布。这是我们拥有不适合其他两类别的长篇文档的地方。

如果您正在考虑在此处添加一个全新的文档,最好先打开一个 GitHub 问题来讨论该想法以及如何最好地组织它。

有关提交给集合文档站点的所有内容,请参阅Ansible 风格指南

文档站点的 RST 文件位于 docs/docsite/rst/ 目录中。一些提交也可能需要编辑 docs/docsite/extra-docs.yml

当提交更改集合文档的拉取请求时,将生成一个新的文档站点并发布到临时站点,并且机器人将在 PR 上发布包含链接的评论。这将使您看到呈现的文档,以帮助发现格式错误。

也可以通过安装一些额外的 Python 要求并运行构建脚本在本地构建文档

$ pushd docs/preview
$ pip install -r requirements.txt
$ ./build.sh

然后,您可以在 docs/preview/build/html 中找到生成的 HTML,并且可以在浏览器中本地打开它们。

在本地运行测试

如果您进行的更改不仅仅是非常小的或一次性的更改,请在本地运行测试,以避免必须为每件事推送提交,并等待 CI 运行测试。

首先,请查看有关测试集合的指南,因为它也适用于此集合。

集成测试

与其他集合不同,我们需要一个 integration_config.yml 文件才能正确运行集成测试,因为这些测试需要外部依赖项(如 Vault 服务器),并且它们需要知道在哪里找到这些依赖项。

如果您过去为这个集合或 hashi_vault 查找插件做出过贡献,您可能会记得集成测试曾经默认在测试过程中下载、提取和运行 Vault 服务器。这种旧模式不再可用

Docker Compose 本地环境

推荐的测试运行方式是将 Vault 和其他依赖项在其各自的容器中运行,通过 docker-compose 设置,并将集成测试单独在其自身的容器中运行。

我们为此目的预定义了一个“localenv”设置角色。

用法

为了方便输入/命令长度,我们将首先进入角色目录

$ pushd tests/integration/targets/setup_localenv_docker

此本地环境既有 Ansible 集合的要求,也有 Python 的要求,所以让我们先解决这些问题

$ pip install -r files/requirements/requirements.txt -c files/requirements/constraints.txt
$ ansible-galaxy collection install -r files/requirements/requirements.yml

要使用所有默认值设置您的 docker-compose 环境

$ ./setup.sh

设置脚本执行以下操作

  1. 为项目模板化一个 docker-compose.yml

  2. 为 Vault 生成私钥和自签名证书。

  3. 模板化一个 Vault 配置文件。

  4. 关闭现有的 compose 项目。

  5. 根据变量(指定的或默认的)定义,启动 compose 项目。

  6. 模板化一个 integration_config.yml 文件,其中包含用于集成测试连接的所有正确设置。

  7. 将集成配置复制到正确的位置如果那里还没有一个(它不会覆盖,以防您进行了自定义更改)。

在您的容器运行后,您现在可以在 docker 中运行测试(在返回到集合根目录后)

$ popd
$ ansible-test integration --docker default --docker-network hashi_vault_default -v

--docker-network 部分很重要,因为它确保 Ansible 测试容器与依赖容器在同一个网络中,这样测试容器可以通过它们的容器名称访问它们。网络名称 hashi_vault_default 来自此角色使用的默认 docker-compose 项目名称 (hashi_vault)。有关更多信息,请参阅 自定义部分

再次运行 setup.sh 可用于重新部署容器,或者如果您愿意,可以直接使用本地工具使用生成的 files/.output/<project_name>/docker-compose.yml

如果再次运行,请记住手动将新生成的 files/.output/integration_config.yml 的内容复制到集成根目录,或者在重新运行设置之前删除根目录中的文件,以便它可以自动复制该文件。

自定义

setup.sh 将您发送给它的任何其他参数传递给它调用的 ansible-playbook 命令,因此您可以使用标准 --extra-vars (或 -e) 选项自定义变量。 有许多高级场景是可能的,但您可能想要覆盖以下几项

  • vault_version – 可以定位存在 docker 容器的任何 Vault 版本(这是容器的标签),默认为 latest

  • docker_compose(默认为 clean,但可以设置为 updownnone
    • up – 类似于运行 docker-compose up(如果项目按应有的方式运行,则无操作)

    • down – 类似于 docker-compose down(销毁项目)

    • clean – (默认) 类似于 docker-compose down,后跟 docker-compose up

    • none – 执行其他任务,包括模板化,但不启动或关闭项目。使用此选项,不需要 community.docker 集合。

  • vault_crypto_force – 默认情况下,此值为 false,因此如果证书和密钥存在,则不会重新生成。设置为 true 将覆盖它们。

  • vault_port_httpvault_port_httpsproxy_port – 所有端口都暴露给主机,因此如果您的主机上已经使用了任何默认端口,您可能需要覆盖这些端口。

  • vault_container_nameproxy_container_name – 这些是它们各自容器的名称,也将是容器网络中使用的 DNS 名称。如果您正在使用默认名称,则可能需要覆盖这些名称。

  • docker_compose_project_name – 不太可能需要更改,但它会影响 docker 网络的名称,这对于您的 ansible-test 调用是必需的,因此值得一提。例如,如果您将其设置为 ansible_hashi_vault,则 docker 网络名称将为 ansible_hashi_vault_default

贡献身份验证方法

在此集合中,身份验证方法在所有插件和模块之间共享,而不是在每个插件和模块中重新实现。这节省了重新发明轮子的精力,避免了因必须跨身份验证方法测试功能而导致的测试膨胀,并提供了始终如一的体验。

文件位置 & 作用域

身份验证方法在 module_utils 中作为类实现,在名为 plugins/module_utils/_auth_method_<method_name>.py 的文件中实现。前导下划线表示该模块实用程序是集合私有的,并且不打算在集合外部使用;这使我们可以在需要时进行更改而无需发布新的主要版本,并明确向潜在的下游用户表明他们不应依赖集合内容之外的这些实用程序。

此外,集合中的所有身份验证方法模块实用程序都必须包含解释这一点的注释,例如

# FOR INTERNAL COLLECTION USE ONLY
# The interfaces in this file are meant for use within the community.hashi_vault collection
# and may not remain stable to outside uses. Changes may be made in ANY release, even a bugfix release.
# See also: https://github.com/ansible/community/issues/539#issuecomment-780839686
# Please open an issue if you have questions about this.

最好查看现有的身份验证方法,以了解它们的实现方式。

类解剖

每个身份验证方法类都应命名为 HashiVaultAuthMethod<MethodName> 并继承自 HashiVaultAuthMethodBase

基类提供了一些常用功能,例如标准化发出警告的方式以及提供用于验证必需选项的常用函数。

身份验证方法必须运行基类的 __init__ 函数。

它必须实现两个方法

  • validate() – 此方法尽一切可能确保满足使用此特定身份验证方法执行身份验证的要求。这可能包括检查必需的选项、验证这些选项的值、从环境中提取附加信息和上下文、准备该信息以供 authenticate() 使用等。一般来说,它不应联系 Vault,并且应尽量减少对外部来源和服务的依赖,但这只是一条指导原则,细节将取决于所讨论的身份验证方法的具体情况。validate() 如果验证失败,则会引发异常。如果成功,则不返回任何内容。

  • authenticate(client, use_token=False) – 此方法执行实际的身份验证,并返回身份验证的 API 结果(其中将包括令牌、租约信息等)。传递 HVAC 客户端对象,以及一个可选参数 use_token,该参数指定客户端的令牌字段是否应设置为身份验证的结果(通常这是需要的)。

身份验证方法类还应包含两个字段

  • NAME – 身份验证方法的名称。

  • OPTIONS – 包含身份验证方法可能使用的每个选项的名称的列表,包括可选选项;此列表不应包含 auth_method 选项。

引发异常和警告

由于身份验证方法在 Ansible 模块和 Ansible 插件之间共享,因此引发的任何异常都必须适用于两者。如果合适,可以引发诸如 KeyError 之类的标准 Python 异常。

在通常会引发 AnsibleError(在插件中)或调用 module.fail_json()(在模块中)的情况下,您可以引发带有错误消息的 HashiVaultValueError。此集合中的插件和模块应期望此类型并采取相应措施。

同样对于警告,由于插件和模块以不同的方式实现警告,因此需要警告的模块实用程序代码会使用警告回调,身份验证方法也是如此。

基类提供了一个 warn() 方法,该方法处理调用在类初始化时指定的回调,因此可以在身份验证方法代码中使用简单的 self.warn()

访问选项

由于身份验证方法在 Ansible 模块和 Ansible 插件之间共享(它们访问选项的方式不同),此集合实现了一个名为 HashiVaultOptionAdapter 的类。此类为必须在插件和模块中工作的代码提供了一个访问选项值的标准接口。

它实现了以下方法:

  • get_option(key) – 获取具有指定名称的选项。如果选项不存在,则引发 KeyError

  • get_option_default(key, default=None) – 获取具有指定名称的选项。如果它不存在,则返回 default 的值。

  • set_option(key, value) – 将指定选项 key 的值设置为 value

  • set_option_default(key, default=None) – 返回选项 key 的值。如果该键不存在,则将其值设置为 default 并返回该值。

  • has_option(key) – 如果指定选项存在则返回 True ( None 值被视为存在)。

  • set_options(**kwargs) – 将选项设置为 kwargs 中指定的键/值对。

  • get_options(*args) – 返回 args 中指定的选项名称的字典。

  • get_filtered_options(filter, *args) – 如果可调用对象 filter (它传入 keyvalue) 对于给定的键/值对返回 True,则返回 args 中指定的选项名称的字典。

  • get_filled_options(*args) – 返回 *args 中指定的且不为 None 的选项名称的字典。

身份验证器

HashiVaultAuthenticator 类是集合中大多数内容处理身份验证的方式,而不是必须直接引用每个单独的身份验证方法。因此,需要为每个新的身份验证方法修改 _authenticator.py,因为它导入并直接引用每个类。请参阅此类的实现,以查找需要修改的位置。

身份验证方法选项和文档

由于身份验证方法在集合内容之间共享,因此它们的选项在 doc_fragment 插件中记录。由于许多选项最终会在许多身份验证方法之间共享(例如 role_idusernamepassword),我们不会为每个身份验证方法设置单独的文档片段,因为这将导致重复的选项文档。

相反,所有身份验证方法的选项都在 plugins/doc_fragments/auth.py 中。

这包含标准的 DOCUMENTATION 字段,以及 PLUGINS 字段。这种拆分的原因是,文档的某些部分仅适用于插件;即 envinivars 条目。

DOCUMENTATION 应包含两者共有的所有字段,如 descriptiontypeversion_addedrequired 等,而任何特定于插件的内容都应放入 PLUGINS 中。

由于插件和模块将引用文档片段,因此通常不需要直接修改内容中的文档字符串;如果似乎有必要,请提出问题进行讨论。

在可能的情况下,我们应该为指定选项提供 envinivars 替代方案,以便为插件提供最大的灵活性。有时,这些没有意义,例如在 ini 中提供 token (一个敏感值)。

在决定为身份验证方法实现新选项时,请考虑是否可以或应该重用现有选项。如果需要新选项,请考虑将其名称限定在身份验证方法中,以便将其与当前或将来可能在其他上下文中造成混淆的选项名称区分开来。

例如,cert_auth_public_keycert_auth_private_key 这样命名是为了防止它们与与 Vault 连接相关的其他证书选项或其他特定插件或模块可能需要密钥对的上下文混淆。

测试身份验证方法

由于身份验证方法在整个集合中共享,我们希望它们得到非常好的测试。身份验证方法同时具有单元测试和集成测试,并且它们的组合应该使我们非常有信心这些方法按预期工作。

单元测试

单元测试允许我们检查一些在集成测试中难以有效测试的功能,例如检查每个可能的选项组合是否按预期工作,或模拟我们无法轻易重现的条件。各种场景的覆盖率应该很广泛,并且它们的细节或复杂程度将取决于身份验证方法本身的复杂性。强烈建议查看现有示例。

提供了一个 pytest fixture 来加载包含示例 Vault API 响应的 fixture 文件。使用这些可以在单元测试中模拟 HVAC 身份验证调用。

集成测试

我们的集成测试提供了一个正在运行的 Vault 服务器,有了它,我们(理论上)可以设置我们想要的任何身份验证方法。在实践中,身份验证方法通常需要外部服务才能集成。在可能的情况下,我们应该考虑设置此类外部服务,以便我们可以创建有意义的真实集成并对其进行测试。

然而,通常情况下,这是不可能的,或者很困难。我们必须考虑到测试不仅在 CI 中运行,而且还应该能够在本地运行。

模拟集成

我们在集成测试设置中实现了 MMock (Monster Mock) 来帮助实现这一点。此服务器设置为将其请求代理到测试 Vault 服务器,但您可以编写配置,使其可以为特定请求返回不同的数据。通过仔细构造这些响应,我们可以模拟 Vault API 对特定身份验证方法的登录请求的响应,并模拟其失败。有了这些,我们就可以运行集成测试,希望这些测试可以为我们提供一些保证,确保我们正在正确地实现它。

测试插件和模块用法

身份验证方法可从模块和插件中使用,因此身份验证方法的集成测试必须通过插件和模块对其进行测试。

我们提供了专门用于在集成测试中测试身份验证方法的自定义模块和插件。这些是简化的实现,但它们使用了所有内容都应使用的通用代码,并且可以设置为返回有关登录过程的一些有用信息。有关详细信息,请参阅现有测试。

测试覆盖率

在 CI 中,我们使用 CodeCov 来跟踪覆盖率。我们还在覆盖率中设置了一些特定的“标签”,其中一个是将单个身份验证方法标记为集成测试的目标。这在 CI 中自动发生,但是新的身份验证方法需要在 codecov.yml 中添加一个条目,该条目将覆盖率标志映射到实现身份验证方法的文件。例如

flags:
  target_auth_aws_iam:
    paths:
      - plugins/module_utils/_auth_method_aws_iam.py