我们希望听到您的意见!帮助我们深入了解 Ansible 生态系统的现状。
参加 Ansible 项目调查 2024

AWS Ansible 模块开发和社区贡献入门

AWS Ansible 模块开发和社区贡献入门

我们经常从云管理员和开发人员那里听到他们想回馈 Ansible 并利用他们的知识来造福社区,但他们不知道如何开始。许多人甚至可能已经在他们的本地环境中包含了新的 Ansible 模块或插件,并希望将其包含在上游以供更广泛地使用。

幸运的是,作为 Ansible 贡献者入门并不需要太多。如果您已经在使用 Ansible AWS 模块,有很多方法可以利用您现有的知识、技能和经验做出贡献。如果您需要一些关于在何处贡献的建议,请查看以下内容

  • 创建集成测试:为模块创建缺失的测试 是入门的好方法,集成测试只是 Ansible 任务!
  • 模块移植:如果您熟悉 boto3 Python 库,还有一个模块积压 需要从 boto2 移植到 boto3。
  • 仓库问题分类:当然,总是有开放的 Github问题 和拉取请求。测试错误或补丁并提供有关您的用例和体验的反馈非常有价值。

boto3

从 Ansible 2.10 开始,AWS 模块已从Ansible GitHub 仓库 迁移到两个新的集合 仓库。

由 Ansible 维护的集合(amazon.aws)包含由 Ansible 云团队管理的模块、插件和模块实用程序,并包含在下游 Red Hat Ansible Automation Platform 产品中。

社区集合 (community.aws) 包含由 Ansible 社区支持的模块和插件。社区开发的新模块和插件应建议提交到 community.aws。此集合中稳定并符合其他接受标准的内容有可能被提升并迁移到 amazon.aws。

有关如何为任何由 Ansible 维护的集合(包括 AWS 集合)做出贡献的更多信息,请参阅docs.ansible.com 上的“为由 Ansible 维护的集合做出贡献”部分

AWS 模块开发基础

首先,确保您已阅读 Ansible 开发人员指南中的Ansible Amazon AWS 模块开发指南 部分。以下是一些需要记住的事情

如果模块需要轮询 API 并等待返回特定状态才能继续,请向等待器 添加到 amazon.aws 集合中的waiters.py 文件中,而不是在模块内编写循环。例如,ec2_vpc_subnet 模块支持 wait 参数。当为 true 时,这会指示模块在返回之前等待资源处于预期状态。此模块代码如下所示

if module.params['wait']:
    handle_waiter(conn, module, 'subnet_exists', {'SubnetIds': [subnet['id']]}, start_time)

以及相应的等待器

        "SubnetExists": {
            "delay": 5,
            "maxAttempts": 40,
            "operation": "DescribeSubnets",
            "acceptors": [
                {
                    "matcher": "path",
                    "expected": True,
                    "argument": "length(Subnets[]) > `0`",
                    "state": "success"
                },
                {
                    "matcher": "error",
                    "expected": "InvalidSubnetID.NotFound",
                    "state": "retry"
                },
            ]
        },

这会轮询 EC2 API 以获取 describe_subnets(SubnetIds=[subnet['id']]),直到返回的子网列表大于零,然后继续。如果返回 InvalidSubnetID.NotFound 错误,则这是预期响应,等待器代码将继续执行。

当 boto 返回分页结果时使用分页器,并从分页器的 .build_full_result() 方法构建结果,而不是编写循环。

确保在您的 except 块中处理 ClientErrorBotoCoreError

except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
    module.fail_json_aws(e, msg="Couldn't create subnet")

所有新模块都应尽可能支持check_mode

Ansible 努力提供幂等性。但是,有时这与 AWS 服务的操作方式不一致。考虑用户如何通过 Ansible 任务与服务交互,以及如果他们多次运行相同的任务会发生什么。将发出哪些 API 调用?Ansible 在后续任务执行中将报告哪些更改状态?

尽可能避免在模块中硬编码数据。有时这是不可避免的,但如果您的贡献包含实例类型的硬编码列表或硬编码的分区,这很可能在代码审查中被提出 - 例如,arn:aws: 将不匹配 GovCloud 或中国区域,您的模块将不适用于这些区域的用户。如果您已经确定没有合理的方法可以避免对某些内容进行硬编码,请在拉取请求中提及您的发现。

模块实用程序

amazon.aws 集合中提供大量用于与 AWS 交互的 module_utils

$ ls plugins/module_utils/
acm.py  batch.py  cloudfront_facts.py  cloud.py  core.py  direct_connect.py  ec2.py  elb_utils.py  elbv2.py  iam.py  __init__.py  rds.py  s3.py  urls.py  waf.py  waiters.py

值得注意的是,module_utils/core.py 包含 AnsibleAWSModule(),它是所有新模块所需的基类。这提供了一些不错的助手,例如 client() 设置、fail_json_aws() 方法(它将 boto 异常转换为友好的错误消息并处理 Python2 和 Python3 的错误消息类型转换),并且该类将为您处理 boto 库导入检查。

AWS API 倾向于使用和返回驼峰式 值,而 Ansible 更喜欢蛇形式amazon.aws.module_utils.ec2 中提供了在这些之间进行转换的帮助程序,包括 ansible_dict_to_boto3_filter_list()boto3_tag_list_to_ansible_dict() 以及许多与标签和策略相关的函数。

集成测试

AWS 集合主要依靠功能性集成测试 通过在 AWS 上创建、修改和删除资源来练习模块和插件代码。测试套件位于包含正在测试的模块的集合仓库中。测试的首选样式看起来像一个以模块命名的角色,每个模块都有一个测试套件。有时将多个模块的测试合并到一个测试套件中是有意义的,例如当存在紧密耦合的服务依赖项时。这些通常会以正在测试的主要模块或服务命名。例如,*_info 模块可能会与它们提供信息的 службa 共享一个测试。测试目录根目录中的别名文件控制各种设置,包括哪些测试被别名为该测试角色。

tests/integration/targets/ecs_cluster$ ls
aliases  defaults  files  meta  tasks

tests/integration/targets/ecs_cluster$ cat aliases
cloud/aws
ecs_service_info
ecs_task
ecs_taskdefinition
ecs_taskdefinition_info
unsupported

在这种情况下,几个模块被组合成一个测试,因为必须在创建 ecs_taskdefinition 之前创建 ecs_cluster。这里存在强依赖性。

您可能还会注意到,ECS 目前在 Ansible CI 环境中不受支持。这可能有几个原因,但最常见的原因是我们不允许在 CI AWS 帐户中无限制地使用资源。我们必须创建IAM 策略,这些策略允许进行测试覆盖的最低可能的访问权限。其他原因可能是由于模块需要我们在 CI 中无法使用的资源,例如联合身份提供者。有关更多信息,请参阅下面的“CI 策略和 Terminator Lambda”部分。

您可能看到的另一个测试套件状态是不稳定。这意味着该测试已被观察到具有较高的瞬态失败率。常见原因包括需要等待资源达到特定状态才能继续,或者测试运行时间过长并且超过了测试计时器。这些可能需要对模块代码或测试进行重构,以使其更稳定可靠。不稳定的测试只有在它们覆盖的模块被修改时才会运行,如果它们失败可能会重试。如果您发现您喜欢测试,这是一个开始的好地方!

集成测试通常应检查以下任务或函数,无论是 **有** 还是 **没有** 检查模式

  • 资源创建
  • 再次创建资源(幂等性)
  • 资源修改
  • 再次修改资源(幂等性)
  • 资源删除
  • 资源删除(不存在的资源)

在创建集成测试任务文件时使用 module_defaults 用于凭据,而不是为每个任务重复这些参数。如果需要测试模块如何处理错误凭据、缺少区域参数等,可以在每个任务中覆盖 module_defaults 中指定的值。

- name: set connection information for aws modules and run tasks
  module_defaults:
    group/aws:
      aws_access_key: "{{ aws_access_key }}"
      aws_secret_key: "{{ aws_secret_key }}"
      security_token: "{{ security_token | default(omit) }}"
      region: "{{ aws_region }}"

  block:

  - name: Test Handling of Bad Region
    ec2_instance:
    region: "us-nonexistent-7"
      ... params …

  - name: Do Something
    ec2_instance:
      ... params ...

  - name: Do Something Else
    ec2_instance:
      ... params ...

集成测试应利用,在一个或多个块中包含测试任务,以及一个最终的 always: 块,该块删除测试创建的所有资源。

单元测试

虽然大多数模块都使用集成测试进行测试,但有时这是不可行的。例如,测试 AWS Direct Connect 时就是这样。community.aws.aws_direct_connect* 模块可用于在 AWS 和私有数据中心之间建立网络转接链路。这不是可以在 CI 测试系统中简单或重复完成的任务。对于实际上无法进行集成测试的模块,我们确实需要单元测试 才能包含在任何 AWS Ansible 集合中。placebo Python 库提供了一个很好的机制来记录和模拟 boto3 API 响应,并且在可能的情况下优先于编写和维护 AWS 固定装置。

CI 策略和 Terminator Lambda

Ansible AWS CI 环境具有安全措施和特定工具,以确保资源受到适当限制,并且测试资源在合理的时间内清理。这些工具位于aws-terminator 仓库中。该仓库有三个主要部分需要注意

  1. aws/policy/ 目录
  2. aws/terminator/ 目录
  3. hacking/ 目录

aws/policy/ 目录包含 Ansible CI 服务使用的 IAM 策略。我们通常尝试定义执行全面集成测试覆盖所需的最小 AWS IAM 操作和资源。例如,我们不是启用 ec2:*,而是拥有多个语句 ID,即 Sid,它们为不同的资源规范指定不同的操作。

我们允许在 CI 运行的区域中相当广泛地使用 ec2:DescribeImages

  Resource:
      - "*"
    Condition:
      StringEquals:
        ec2:Region:
          - '{{ aws_region }}'

但对可以通过 CI 启动或运行的实例类型更加严格

- Sid: AllowEc2RunInstancesInstanceType
    Effect: Allow
    Action:
      - ec2:RunInstances
      - ec2:StartInstances
    Resource:
      - arn:aws:ec2:us-east-1:{{ aws_account_id }}:instance/*
    Condition:
      StringEquals:
        ec2:InstanceType:
          - t2.nano
          - t2.micro
          - t3.nano
          - t3.micro
          - m1.large  # lowest cost instance type with EBS optimization supported

aws/terminator/ 目录包含 terminator 应用程序,我们将其部署到 AWS Lambda。  它充当清理服务,在任何 CI 作业无法删除其创建的资源时,它会发挥作用。  关于编写新的 terminator 类的信息可以在 terminator 的 README 中找到。

hacking/ 目录包含一个剧本和两组策略,供贡献者在他们自己的 AWS 账户中使用。  aws_config/setup-iam.yml 剧本创建 IAM 策略并将它们与两个 iam_groups 关联。然后,可以将这些组与您自己的适当用户关联

  • ansible-integration-ci:该组反映了 AWS 集合 CI 使用的权限
  • ansible-integration-unsupported:该组在 'CI' 权限的基础上分配了额外的权限,这些权限是运行 '不支持' 测试所需的

setup-iam.yml 剧本中记录了将这些组和策略部署到您的 AWS 用户的用法信息。

本地测试

您现在已经编写了代码和测试用例,但您希望在推送到 GitHub 并将更改发送到 CI 之前在本地运行测试。  太棒了!  您需要一个 AWS 账户的凭据,以及一些设置步骤。 

Ansible 包含一个 CLI 实用程序来运行集成测试。  您可以在环境中设置 boto 配置文件,或使用凭据配置文件来验证 AWS。  示例配置 文件由 Ansible 附带的 ansible-test 应用程序提供。  将此文件复制到本地检出的集合存储库中的 tests/integration/cloud-config-aws.ini,并填写您的 AWS 账户详细信息,用于 @ACCESS_KEY@SECRET_KEY@SECURITY_TOKEN@REGION。

注意:两个 AWS 集合存储库都有一个 tests/.gitignore 文件,在检入代码时会忽略此文件路径,但您在将 AWS 凭据存储到磁盘或存储库目录时应始终保持警惕。

如果您已经在本地机器上安装了 Ansible,则 ansible-test 应该已经在您的 PATH 中。  如果没有,您可以从 Ansible 项目的本地检出中运行它。

git clone https://github.com/ansible/ansible.git
cd ansible/
source ansible/hacking/env-setup

您还需要确保在您的 COLLECTIONS_PATHS 中安装并访问了所有集合依赖项。  集合依赖项列在集合的 tests/requirements.yml 文件中,可以使用 ansible-galaxy collection install 命令安装。

您现在可以从集合存储库运行集成测试

cd ~/src/collections/ansible_collections/amazon/aws
ansible-test integration ec2_group

默认情况下,不稳定或不受支持的测试不会执行。  要运行这些类型的测试,您可以向 ansible-test 传递额外的标志

ansible-test integration ec2_group --allow-unstable  --allow-unsupported

如果您希望在容器中运行测试,ansible-test 可以自动检索并运行一个默认测试镜像,该镜像包含 AWS 测试所需的 Python 库。  这可以通过提供 --docker 标志来拉取和运行。  (Docker 必须已在您的本地系统上安装和配置。)

ansible-test integration ec2_group --allow-unstable  --allow-unsupported --docker

测试容器镜像附带所有 Ansible 支持的 Python 版本。  要指定特定 Python 版本,例如 3.7,请使用以下命令进行测试

ansible-test integration ec2_group --allow-unstable  --allow-unsupported --docker --python 3.7

注意:集成测试将在指定的 AWS 账户中创建真实资源,受该资源和区域的 AWS 定价约束。  现有测试应尽一切努力在测试运行结束时删除资源,但请确保在执行测试套件后检查所有创建的资源是否已成功删除,以防止出现意外账单。  这在开发新的测试套件或添加测试的始终清理块尚未涵盖的新资源时尤其推荐。  

注意:在处理可能泄露 AWS 账户访问权限或资源的 IAM、安全组和其他访问控制时,请务必谨慎。

提交更改

当您的更改准备提交时,在 拉取请求 (PR) 中打开 适当的 AWS 集合 的 GitHub 存储库。  Shippable CI 会自动运行测试并将结果报告回 PR。  如果您的更改是针对新的模块或测试新的 AWS 资源或操作,您可能会在测试中看到权限失败。  在这种情况下,您还需要在 mattclay/aws-terminator 存储库 中打开一个 PR,以添加 IAM 权限,并可能添加一个 Terminator 类 来支持测试新功能,如本文的“CI 策略和 Terminator Lambda”部分所述。  Ansible AWS 社区成员将对您的贡献进行分类和审查,并提供他们对提交的任何反馈。  

下一步和资源

对开源项目的贡献乍一看可能令人望而生畏,但希望这篇博文能提供一个关于如何为 AWS Ansible 集合做出贡献的很好的技术资源。如果您在贡献过程中需要帮助,您可以在 Freenode IRC 的 #ansible-aws 频道中找到 Ansible AWS 社区。

恭喜您并欢迎您,您现在是 Ansible 项目的贡献者!