介绍
配置即使格式规范且结构有效,也可能错误地标识报告环境。在信任交付计划之前,你的团队需要对一个小型存储模块的输入、输出和计划标签进行可执行检查。
你将编写模块约定,测试两个受支持环境及一个不受支持的输入,并让真正的业务缺陷阻止作业继续执行。你应已了解模块变量和输出,以及上一个实验中的格式检查和验证关卡。现有参考存储必须保持不变。
与认证考试的关联
| 认证 | 考试任务 | 实践内容 |
|---|---|---|
| DevOps Engineer – Professional (DOP-C02) | 任务 1.2 | 将可执行模块断言集成到自动化质量关卡中的基础实践。 |

定义输入约定
在这一步中,你将定义存储模块接受哪些环境输入。输入验证会在创建资源计划之前拒绝不受支持的请求。
进入提供的模块并加载已准备好的工具。
cd /home/labex/project/report-module
source environment.sh
阅读其接口和资源实现。
cat variables.tf main.tf outputs.tf pipeline.json
environment 选择 preview 或 review;bucket_prefix 区分调用方的存储名称。存储桶名称组合这两个输入,Environment 标签和输出必须标识请求的环境。提供的 main.tf、outputs.tf、provider.tf 和 run-job.py 都必须保留:其最终内容应保持不变。你的修改应位于 variables.tf、tests/ 和 pipeline.json 中。
为 environment 添加 validation 块。contains 检查值是否在受支持列表内;condition 为 false 时会产生所提供的错误消息。带引号的 here-document 原样写入 HCL。
cat > variables.tf <<'EOF'
variable "environment" {
type = string
default = "preview"
validation {
condition = contains(["preview", "review"], var.environment)
error_message = "Choose preview or review."
}
}
variable "bucket_prefix" {
type = string
default = "delivery-contract"
}
EOF
格式化你的更改。
terraform fmt
尝试一个不受支持的请求。-var 为本次调用提供值。此命令预期返回原生命令退出码 1;它必须显示 Choose preview or review.,而不是提出创建 production 存储桶。
terraform plan -input=false -var=environment=production
验证定义了接受的接口。在下一步中,测试将覆盖两个接受的值及这项预期拒绝。
编写断言并观察实际失败
在这一步中,你将使用原生 terraform test 检查模块的计划行为。格式检查和验证可能接受硬编码但错误的输出;断言则将计算结果与请求输入进行比较。
Terraform 读取 tests/ 下的 .tftest.hcl 文件。每个 run 是独立的测试场景。显式 command = plan 评估计划但不应用它。将测试文件以 .tftest.hcl 形式保存在 tests/ 下。本实验每次运行都必须使用 plan 模式及提供的 provider 和模块;替代模块、provider mock 和覆盖不在任务范围内。保持 bucket_prefix 继承自测试套件输入:不要在文件层级或 run 层级的测试变量中为其赋值,以便调用方选择另一个合法前缀。这些测试检查已知计划值,不验证实际创建、运行时权限或已部署应用。
创建测试目录。mkdir -p 会在需要时创建目录,也接受已存在的目录。
mkdir -p tests
为默认环境和另一个环境编写断言。var 指运行输入,资源引用检查计划值,output 指模块输出。expect_failures 要求指定的变量验证失败;它不会将任意 provider 或网络错误变为测试成功。
cat > tests/contract.tftest.hcl <<'EOF'
run "preview_contract" {
command = plan
assert {
condition = output.environment == var.environment
error_message = "The output must identify the requested environment."
}
assert {
condition = aws_s3_bucket.reports.bucket == "${var.bucket_prefix}-${var.environment}"
error_message = "The bucket name must use the requested prefix and environment."
}
assert {
condition = aws_s3_bucket.reports.tags.Environment == var.environment
error_message = "The planned tag must identify the requested environment."
}
}
run "review_contract" {
command = plan
variables { environment = "review" }
assert {
condition = output.environment == var.environment
error_message = "The alternate environment must not use the default label."
}
assert {
condition = aws_s3_bucket.reports.bucket == "${var.bucket_prefix}-${var.environment}"
error_message = "The alternate bucket must use its requested inputs."
}
assert {
condition = aws_s3_bucket.reports.tags.Environment == var.environment
error_message = "The alternate environment tag must match its input."
}
}
run "unsupported_environment" {
command = plan
variables { environment = "production" }
expect_failures = [var.environment]
}
EOF
递归格式化测试和模块文件。
terraform fmt -recursive
运行测试套件。
terraform test
预期三个运行通过,并显示成功摘要。不会部署测试存储桶。输入和输出值来自使用已配置 provider 的实际 Terraform 计划。
证明断言能捕获一种可能出现的缺陷。暂时仅将环境输出替换为固定的默认标签;保留存储桶名称输出。
cat > outputs.tf <<'EOF'
output "environment" {
value = "preview"
}
output "bucket_name" {
value = aws_s3_bucket.reports.bucket
}
EOF
再次运行套件。此命令预期返回原生命令退出码 1。另一个环境的断言应失败,说明有效 HCL 仍可能违反模块约定。
terraform test
从 Git 恢复提供的输出实现。这会移除临时缺陷,同时保留你编写的验证和测试。
git restore -- outputs.tf
使用相同断言测试已修正的实现。
terraform test
所有运行都应再次通过。测试必须接受合法输入,而不只是识别一个默认输出。
让测试阻止交付作业继续执行
在这一步中,你将把模块测试放在静态关卡和计划生成之间。执行器已预先提供;编辑阶段配置,不要更改 run-job.py。
在 fmt/validate 之后、plan 之前添加 test。
cat > pipeline.json <<'EOF'
{"stages":["fmt","validate","test","plan"]}
EOF
运行完整作业。
python3 run-job.py
四个阶段都应通过。最终计划可能返回原生命令退出码 2,因为此模块尚未应用;运行器会将它视为成功预览。测试失败则必须在 plan 阶段之前停止作业。此作业没有 apply 阶段。
使用另一个合法存储桶前缀运行套件。此处的 -var 改变套件输入,不编辑模块或测试。
terraform test -var=bucket_prefix=audit-contract
同样的三个运行都应通过。本练习要求每个运行中的 bucket_prefix 均继承自调用方:不要在测试文件或 run 层级的 variables 块中为其赋值。这样的赋值会覆盖 CLI 测试输入,使此命令无法测试请求的前缀。环境覆盖仍然是测试两个受支持环境所必需的。当调用方选择另一个有效名称时,基于请求输入构建的断言仍然有效。
直接从存储中读取现有参考报告。这些预先准备的存储桶属于另一个环境;不要销毁或修改它们。
aws s3 cp s3://delivery-reference-reports/report.txt -
内容应仍为 reference report v1。也检查相邻数据。
aws s3 cp s3://delivery-reference-neighbor/keep.txt -
预期看到 preserve contract neighbor。测试没有创建存储资源,因此没有需要销毁的测试资源栈。保留输入约定、测试和流水线配置以供最终检查;临时输出缺陷已经移除。
总结
你定义了受支持的输入约定,使用原生 plan 模式 Terraform 测试将计划输出和标签与输入比较,并观察到实际断言失败。随后,你将测试纳入交付质量关卡,同时保留现有数据和业务状态。
plan 模式测试不能证明部署后的行为。后续交付步骤会将这些检查与经过审查的具体计划及实际受控应用相结合。有关本实验所用机制,请参阅官方 Terraform test 参考文档和测试配置文档。



