尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Terraform Equivalence Testing 指南:用 E2E 快照对比守护命令输出行为

Terraform Equivalence Testing 指南:用 E2E 快照对比守护命令输出行为 Terraform Equivalence Testing 指南用 E2E 快照对比守护命令输出行为【免费下载链接】terraformTerraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.项目地址: https://gitcode.com/GitHub_Trending/te/terraform导读Equivalence testing等价性测试是 Terraform 仓库中一套独立于普通单元测试的端到端E2E测试体系它通过固定运行环境下的 Terraform CLI 命令输出快照验证「代码库变更不会导致命令输出以非预期方式发生变化」。本文将基于 testing/equivalence-tests/README.md 与其配套的 40 组真实测试用例、CI 工作流文件完整讲解该框架的定位、目录组织、测试用例编写格式、本地diff/update运行方式以及 CI 自动化闭环帮助你快速理解、运行乃至新增等价性测试用例。Equivalence Testing 是什么为什么 Terraform 需要它Terraform 是一个将 API 声明式编排为配置文件的 IaC 工具其输出如plan、state、机器可读的plan.json会被大量下游工具与自动化流程消费。当核心代码被重构、升级时维护者最担心的问题是逻辑没变但输出悄悄变了。Equivalence testing 正是为这一问题设计的。按仓库 README 的定义它是一组 E2E 测试用于验证Terraform 命令的输出不会以意外方式改变——测试通过「代码库变更前后各运行一次 Terraform 命令然后对比输出」来判断行为是否保持一致。与传统单元测试断言「某个函数返回值是否符合预期」不同等价性测试断言的是**「变更前后输出是否等价」**因此它覆盖terraform plan、terraform apply等完整命令链路而非单个内部函数产出可被 diff 的基准文件golden files供人审阅天然适合捕捉重构导致的格式化、排序、序列化层面的隐性回归。需要特别说明的是等价性测试框架本身是独立于本仓库的工程由 github.com/hashicorp/terraform-equivalence-testing说明该链接指向外部独立仓库需另行下载二进制使用提供本仓库testing/equivalence-tests/目录只承载测试用例与基准输出。目录结构测试用例与基准输出的分离从仓库目录布局看testing/equivalence-tests该体系由两个平级子目录构成职责非常清晰testing/equivalence-tests/ ├── README.md # 本文主体文档 ├── tests/ # 测试用例定义每个用例一个独立子目录 │ ├── basic_list/ │ ├── basic_list_update/ │ ├── drift_simple/ │ ├── moved_simple/ │ ├── ... └── outputs/ # 基准输出 golden files与 tests 一一对应 ├── basic_list/ ├── basic_list_update/ ├── ...tests/存放每个测试的输入配置.tf配置、.tfstate初始状态、spec.json元数据等outputs/存放该测试的参考输出例如 outputs/basic_list 目录下包含outputs/basic_list/ ├── plan # 人类可读的 plan 文本 ├── plan.json # 机器可读的 JSON plan ├── state # 人类可读的 state 文本 ├── state.json # 机器可读的 JSON state └── apply.json # apply 输出的 JSON 表达测试运行时框架会用「当前构建的 Terraform 二进制」重新执行命令产出新的输出并与outputs/下的基准逐项 diff任何差异都会被报告。从用例命名即可看出这套体系覆盖的场景矩阵非常广按主题可归纳为基础类型与集合语义basic_list、basic_map、basic_set、basic_json_string_update、basic_multiline_string_update、nested_list、nested_map、nested_objects、nested_set、simple_object及各自的_empty/_null/_update变体生命周期行为fully_populated_complex及其_destroy、_update漂移drift处理drift_simple、drift_relevant_attributes、drift_refresh_only资源迁移movedmoved_simple、moved_with_drift、moved_with_refresh_only、moved_with_update本地 provider 与 null providerlocal_provider_basic、local_provider_update、local_provider_delete、null_provider_update、null_provider_delete结构替换replacementreplace_within_list/map/object/set、simple_object_replace其他data_read数据源读取、multiple_block_types、variables_and_outputs。这些用例共同覆盖了 plan 中 replace替换、update-in-place就地更新、destroy销毁等动作的输出表达。测试用例的构成一份用例由哪些文件组成以 tests/basic_list 为例一个典型的用例目录包含tests/basic_list/ ├── main.tf # Terraform 配置 ├── spec.json # 测试元数据 └── dynamic_resources.json # 动态 Provider Schema 定义1.main.tf测试配置测试配置使用 HashiCorp 官方的tfcoremock核心 mockprovider 来构造资源无需真实云厂商。配置内容如下terraform { required_providers { tfcoremock { source hashicorp/tfcoremock version 0.1.1 } } } provider tfcoremock {} resource tfcoremock_list list { id 985820B3-ACF9-4F00-94AD-F81C5EA33663 list [ 9C2BE420-042D-440A-96E9-75565341C994, 3EC6EB1F-E372-46C3-A069-00D6E82EC1E1, D01290F6-2D3A-45FA-B006-DAA80F6D31F6, ] }可见测试资源的id与集合元素都采用固定的 UUID 风格字符串目的是让输出确定性可复现从而让 diff 具备意义。2.dynamic_resources.json为 mock provider 声明资源 Schema该文件定义了tfcoremock_list这类资源的属性结构例如声明一个名为list的可选属性其元素类型为 string{ tfcoremock_list: { attributes: { list: { type: list, optional: true, list: { type: string } } } } }借助这份 JSON等价性测试可以在不依赖任何真实 provider 的情况下构造出任意嵌套深度list/map/set/object的资源类型这正是nested_list、nested_set、replace_within_object等复杂用例得以成立的基础。3.spec.json测试元数据每个用例的根目录都有一份spec.json用于声明描述、额外携带文件与忽略字段。以 tests/basic_list/spec.json 为例{ description: basic test covering creation of a single list, include_files: [], ignore_fields: {} }三个字段含义如下description用例的意图描述供审阅者理解该用例覆盖的行为include_files需要随测试一同携带的附加文件列表例如某些 provider 需要额外 schema 文件空数组表示不需要ignore_fields声明需要忽略对比的字段路径集合用于容忍输出中天然不稳定的字段如随机 ID、时间戳。当字段不可控时必须在此处显式忽略否则测试会误报。4.terraform.tfstate更新/漂移类用例的初始状态对于_update、drift_*、moved_*这类需要「先有存量再变更」的用例目录中还会出现一份预置的 terraform.tfstate例如 tests/basic_list_update 的用例先用旧状态的三个元素9C2BE420…、3EC6EB1F…、D01290F6…出发再通过main.tf中新增/删除元素来驱动输出变化{ version: 4, terraform_version: 1.3.6, serial: 1, resources: [ { mode: managed, type: tfcoremock_list, name: list, provider: provider[\registry.terraform.io/hashicorp/tfcoremock\], instances: [ { schema_version: 0, attributes: { id: 985820B3-ACF9-4F00-94AD-F81C5EA33663, list: [ 9C2BE420-042D-440A-96E9-75565341C994, 3EC6EB1F-E372-46C3-A069-00D6E82EC1E1, D01290F6-2D3A-45FA-B006-DAA80F6D31F6 ] }, sensitive_attributes: [] } ] } ], check_results: null }对比 tests/basic_list_update/main.tf 可以发现配置中移除了3EC6EB1F-…元素并新增了9B9F3ADF-…元素从而精确构造出一个「集合元素增删」的 diff 场景。variables_and_outputs用例则额外使用.tfvars文件注入变量覆盖变量与输出交互的输出表达。本地运行diff 与 update 命令按 README 说明执行测试前需要先从terraform-equivalence-testing工程下载对应平台的可执行二进制然后运行其中的diff或update命令。diff对比当前与基准diff命令执行测试并输出「当前运行结果」与「上一轮基准输出」之间的全部差异。它用于回答「这次代码改动是否改变了命令输出」。update刷新基准update命令执行测试并将新的运行结果写回outputs/基准文件。它用于确认「输出变化是预期内的」之后把新输出固化为新的参照标准。从仓库 CI 脚本 equivalence-test-diff.yml 可以看到框架二进制的下载与调用细节当前仓库 CI 固定使用的框架版本为0.5.0目标平台linux/amd64# 1) 下载测试框架二进制 ./.github/scripts/equivalence-test.sh download_equivalence_test_binary \ 0.5.0 \ ./bin/equivalence-tests \ linux \ amd64 # 2) 用当前源码构建 Terraform 二进制 ./.github/scripts/equivalence-test.sh build_terraform_binary ./bin/terraform # 3) 运行等价性测试diff 模式 ./bin/equivalence-tests diff \ --teststesting/equivalence-tests/tests \ --goldenstesting/equivalence-tests/outputs \ --binary$(pwd)/bin/terraform关键的三个 CLI 参数--tests指向testing/equivalence-tests/tests测试用例目录--goldens指向testing/equivalence-tests/outputs基准输出目录--binary指向本次被测的 Terraform 可执行文件通常由 CI 用当前 PR 的源码现构建。此外从该 workflow 还可以得知框架的退出码语义退出码含义CI 处理0输出与基准一致无任何额外动作对 PR 作者完全透明1测试失败在 PR 上评论并令任务失败2输出有变化在 PR 上评论提示「等价性测试将被更新请人工核实」这也印证了 README 中「如果框架未检测到变化整个过程对 PR 作者不可见」的描述——只有当退出码非零1 或 2时 CI 才通过gh pr comment在 PR 上留下评论。CI 自动化闭环PR 生命周期中的两种模式README 明确指出等价性测试由 Terraform 的 CI 系统在每个 PR 打开时与每个 PR 关闭时自动执行仓库中对应的 workflow 文件完整落实了这一设计。PR 打开/更新时运行 diff 并评论equivalence-test-diff.yml 监听pull_request的opened、synchronize、ready_for_review、reopened事件checkout 源码 → 安装 Go 工具链下载框架二进制、用当前源码构建 Terraform执行equivalence-tests diff并捕获退出码写入GITHUB_OUTPUT退出码为1在 PR 上评论失败并链接到 CI run任务以失败告终PR 作者应核实变更并确保 diff 符合预期退出码为2在 PR 上评论「等价性测试将被更新请核实变更」退出码为0静默通过不留任何评论。PR 合并到发布分支时运行 update 并开启新 PRequivalence-test-update.yml 基于pull_request_target的closed事件并且加了「仅当 PR 已合并且目标分支为main或形如vX.Y的发布分支时才执行」的前置判断merged${{ github.event.pull_request.merged }} target_branch${{ github.event.pull_request.base.ref }} targets_release_branchfalse if [ $target_branch main ]; then targets_release_branchtrue elif [ $target_branch ~ ^v[0-9]\.[0-9]$ ]; then targets_release_branchtrue fi满足条件后任务调用 equivalence-test action由 .github/scripts/equivalence-test.sh 实现核心逻辑执行update并自动基于合并目标分支创建名为equivalence-testing/PR head 分支名的新分支与新 PR把更新后的基准文件提交上去同时将合并者merged_by设为 reviewernew-branch: equivalence-testing/${{ github.event.pull_request.head.ref }} reviewers: ${{ github.event.pull_request.merged_by.login }} message: Update equivalence test golden files after ${{ github.event.pull_request.html_url }}.这个自动开启的 PR 需要人工审阅——即 README 中强调的「PR 作者应在合并自动化 PR 前复核变更是否符合预期」。注意该模式默认假设「合并到发布分支的变更大多会改变输出」因此用自动updatePR 承接基准刷新把决策权交还给人类评审者。手动触发equivalence-tests-manual除自动流程外equivalence-test-manual-update.yml 提供了workflow_dispatch手动触发入口用于对任意指定分支执行基准更新。它接收三个输入输入是否必填说明target-branch是要对哪个分支执行更新new-branch是为本次结果创建的新分支名equivalence-test-version是默认0.5.0使用的框架版本不带v前缀如0.5.0该 action 同样会创建一个承载更新后 golden files 的新 PR 供人审阅适合在无法直接使用自动流程如历史分支补跑时使用。编写新测试用例的指南按 README 的要求新增测试应写入tests目录每个测试放在一个独立的子目录中并遵循 equivalence testing 框架自身的规范。可以对照仓库中已有的用例来落地具体步骤在testing/equivalence-tests/tests/下新建一个语义清晰的目录命名建议遵循既有约定行为主题[_update|_empty|_null|_replace|_drift...]编写main.tf声明tfcoremockprovider版本与现有用例保持一致如0.1.1并用固定 UUID 值构造资源与集合元素保证输出确定性若用到tfcoremock之外的属性结构编写dynamic_resources.json声明资源 Schema复杂用例list/map/set/object 嵌套或替换可参考 nested_set、replace_within_object 等现成写法编写spec.json认真填写description若配置中引入了不稳定字段随机数、时间等务必通过ignore_fields显式忽略若用例需要先有存量资源更新、漂移、迁移场景预置一份terraform.tfstate并让main.tf与之形成预期的差异先本地用diff模式验证行为确认差异符合预期后再切换到update模式把基准写入outputs/对应目录。新用例放入tests目录后会被 CI 系统自动发现并纳入上述 PR 生命周期流程equivalence-test-diff.yml 以整个testing/equivalence-tests/tests为扫描根无需额外注册。写在最后输出变更的三类结局综合 README 与仓库 CI 实现一次代码变更对等价性测试的影响收敛为三种结局开发者可以据此快速定位自己 PR 的处境输出无变化退出码 0CI 静默通过作者无需任何操作输出有预期变化退出码 2 / diff 有差异CI 评论提示作者应人工审视 diff确认是本次改动带来的合理输出变化输出变化需固化PR 合并后自动 updateCI 自动开启「golden files 更新」PR作者评审后合并即可完成新一轮基准固化。整套机制的精华在于把「人类判断输出变化是否合理」这一不可自动化的环节保留给评审而把「运行、对比、生成差异、提交基准」这类可重复劳动全部自动化。对于正在为 Terraform 贡献代码的开发者当你的 PR 收到等价性测试评论时检查 testing/equivalence-tests/outputs 对应目录下的 diff确认输出变化符合预期即可——这就是该机制希望达到的协作形态。【免费下载链接】terraformTerraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.项目地址: https://gitcode.com/GitHub_Trending/te/terraform创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表