☰
用 Terratest 为 Terraform SSH 基础设施编写自动化测试:terraform-ssh-example 模块实战解析
2026/9/27 9:06:26 网站建设 项目流程
  • 测试
  • 开发工具
  • DevOps
  • 质量保障

【免费下载链接】terratest

Terratest is a Go library that makes it easier to write automated tests for your infrastructure code.

项目地址:https://gitcode.com/gh_mirrors/te/terratest
点击查看免费下载

本篇指南以仓库中的 terraform-ssh-example 示例模块 为主线,讲解如何用 Terratest 为部署了 EC2 实例(含公网/私网两种拓扑)的 Terraform 代码编写端到端自动化测试。读完本文,你将掌握:模块的资源构成与参数含义、手动terraform apply的运行流程,以及测试如何分阶段完成「部署 → SSH 验证 → 清理」,并理解ssh、aws、terraform、teststructure等核心模块在测试链路中的底层实现。

示例模块概览:它部署了什么

该模块是一个刻意保持精简的 AWS Terraform 模块,用于演示如何用 Terratest 为 AWS Terraform 代码编写自动化测试。它会在aws_region变量指定的区域部署两台 EC2 实例:

  • 一台拥有公网 IP(associate_public_ip_address = true),可直接从公网 SSH 访问;
  • 一台只有私网 IP(associate_public_ip_address = false),只能从 VPC 内部访问,测试中通过公网实例作为跳板机(jump host / bastion host)访问它;
  • 两台实例都允许 SSH 请求,监听端口由ssh_port变量控制(默认 22)。

模块对应的自动化测试位于 test/terraform_ssh_example_test.go,它是本文分析的核心。原文档也提示:该示例仍然相当简化,EC2 实例本身并不做什么实际工作;若需要更接近真实世界的完整示例(Terraform 模块 + Web 服务器),可参考 terraform-packer-example。

核心资源定义(main.tf)

在 main.tf 中可以看到完整的资源定义:

  • provider 与版本约束(main.tf):要求 Terraform 版本>= 1.0,AWS Provider 版本~> 5.0;
  • 公网实例aws_instance.example_public(main.tf):使用最新 Ubuntu AMI,associate_public_ip_address = true,绑定安全组与 Key Pair;
  • 私网实例aws_instance.example_private(main.tf):结构相同,但associate_public_ip_address = false,因此只能从 VPC 内部访问;
  • 安全组aws_security_group.example(main.tf):开放 egress 全部流量,ingress 仅允许var.ssh_port端口的 TCP 请求。代码注释特别提醒:示例为了简单放行了0.0.0.0/0的全部来源 IP,真实场景应只允许可信来源(如堡垒机或 VPN);
  • Ubuntu AMI 数据源data.aws_ami.ubuntu(main.tf):指定 Canonical(owner099720109477),并通过 virtualization-type、architecture、image-type 与 name 等过滤器锁定ubuntu-jammy-22.04-amd64-server-*系列镜像,most_recent = true取最新版本。

参数与输出

模块的输入参数定义在 variables.tf,如下表所示:

变量类型是否必填默认值说明
key_pair_namestring是(必填)—关联到 EC2 实例、用于 SSH 访问的 EC2 Key Pair 名称
aws_regionstring否us-east-1部署的 AWS 区域
instance_namestring否terratest-exampleEC2 实例的 Name 标签(同时用作安全组名)
ssh_portnumber否22EC2 实例监听 SSH 请求的端口
instance_typestring否t2.micro运行的 EC2 实例类型

其中key_pair_name是唯一必填项,其余都有合理默认值,方便快速上手。

模块的输出定义在 outputs.tf,共 4 项,供测试脚本通过terraform output读取:

  • public_instance_id/public_instance_ip:公网实例的 ID 与公网 IP;
  • private_instance_id/private_instance_ip:私网实例的 ID 与私网 IP。

手动运行模块

不写测试、直接手动验证时,按以下步骤操作:

  1. 注册 AWS 账号;
  2. 按 AWS CLI 支持的任一方式配置凭证(如设置AWS_ACCESS_KEY_ID与AWS_SECRET_ACCESS_KEY环境变量);如果使用~/.aws/config中的 profile,还需导出AWS_SDK_LOAD_CONFIG为"True";
  3. 安装 Terraform 并将其加入PATH;
  4. 在 examples/terraform-ssh-example 目录下执行terraform init;
  5. 执行terraform apply(会提示输入key_pair_name,或通过-var传入);
  6. 验证完成后执行terraform destroy清理资源。

注意:由于key_pair_name是必填变量,手动 apply 时一般需要提前在 AWS 控制台或 CLI 中创建好 Key Pair,再通过-var key_pair_name=...传入。

编写自动化测试:TestTerraformSshExample 全流程

自动化测试的全部代码在 test/terraform_ssh_example_test.go。它演示了 Terratest 测试的三种典型能力:Terraform 生命周期管理、SSH/SCP 远程验证、按阶段组织测试以便本地快速迭代。

主测试:三个阶段

测试入口TestTerraformSshExample(test/terraform_ssh_example_test.go)以t.Parallel()开头(可与其他测试并行),整体被拆成三个阶段:

  1. teardown(清理):通过defer注册,无论测试成功失败都会执行 —— 先运行terraform destroy销毁资源,再删除测试期间创建的 EC2 Key Pair;
  2. setup(部署):构造 Terraform 配置并执行terraform init+terraform apply;
  3. validate(验证):对公网/私网实例执行 SSH、SSH Agent、SCP 六类验证。

阶段化的价值在于:只要设置SKIP_teardown=true这类环境变量,就能跳过某个阶段,避免本地反复运行时反复销毁/重建资源,从而大幅缩短迭代周期。这正是 Terratest 的 teststructure 模块 提供的RunTestStage能力(modules/teststructure/teststructure.go):它检查SKIP_<stageName>环境变量是否被设置,未设置才执行该阶段。

setup:构造 Terraform Options 与 Key Pair

configureTerraformOptions(test/terraform_ssh_example_test.go)集中展示了 Terratest 的测试习惯:

  • 用random.UniqueID()生成唯一 ID,拼进实例名与 Key Pair 名,避免与账号内已有资源、并行测试互相冲突;
  • 用aws.GetRandomStableRegionContext(modules/aws/region.go)随机但稳定地挑选测试区域,提升代码在不同区域的兼容性验证;
  • 用aws.GetRecommendedInstanceTypeContext(modules/aws/ec2.go)按所选区域推荐可用的实例类型(候选:t2.micro、t3.micro、t2.small、t3.small),规避某些区域缺少特定实例类型的问题;
  • 用aws.CreateAndImportEC2KeyPairContext在测试区域内生成 2048 位 RSA 密钥对并导入 EC2(底层实现见 modules/aws/keypair.go,通过 EC2ImportKeyPairAPI 完成);
  • 用terraform.WithDefaultRetryableErrors(modules/terraform/options.go)包装terraform.Options,为最常见的可重试错误自动重试;
  • 把aws_region、instance_name、instance_type、key_pair_name通过Vars注入 Terraform。

随后 setup 阶段调用terraform.InitAndApplyContext(modules/terraform/apply.go)一次性完成init与apply,出错即测试失败;同时用teststructure.SaveTerraformOptions与aws.SaveEc2KeyPair把 Options 和 Key Pair 序列化保存(实现见 modules/teststructure/save_test_data.go 与 modules/aws/save_test_data.go),供后续阶段通过LoadTerraformOptions/LoadEc2KeyPair反序列化复用。

这里还有一个容易被忽视的细节:测试开头调用了teststructure.CopyTerraformFolderToTemp(t, "../", "examples/terraform-ssh-example")(test/terraform_ssh_example_test.go),把整个示例目录复制到随机临时目录后再运行 Terraform,避免并行测试互相覆盖.terraform工作目录和terraform.tfstate文件(modules/teststructure/teststructure.go)。

validate:对公网实例直接 SSH

testSSHToPublicHost(test/terraform_ssh_example_test.go)演示了最基础的远程验证:

  1. 通过terraform.OutputContext(modules/terraform/output.go)读取public_instance_ip输出;
  2. 构造ssh.Host,指定主机名、SSH 用户名ubuntu(因为实例运行 Ubuntu AMI)与之前创建的 Key Pair;
  3. 用retry.DoWithRetryContext重试ssh.CheckSSHCommandContextE,执行echo -n 'Hello, World',并断言输出等于预期文本。由于实例启动需要一两分钟,测试配置了 30 次重试、每次间隔 5 秒;
  4. 再执行一次echo -n 'Hello, World' && exit 1,验证「命令非零退出」也能被正确捕获并视为错误。

Host结构体与命令执行底层实现在 modules/ssh/ssh.go:Host支持 Key Pair、SSH Agent、密码等多种认证方式(modules/ssh/ssh.go),CheckSSHCommandContextE内部会解析认证方法、建立 SSH 会话并执行命令(modules/ssh/ssh.go)。认证方法按「覆盖 Agent → 本地 Agent → Key Pair → 密码」的优先级拼接(modules/ssh/ssh.go),全部缺失时返回ErrNoAuthMethod。

validate:经跳板机 SSH 到私网实例

testSSHToPrivateHost(test/terraform_ssh_example_test.go)展示了 Terratest 的跳板机(bastion/jump host)能力:

  • 私网实例的 IP 不通过 Terraform 输出获取,而是用terraform.OutputContext取private_instance_id,再调用aws.GetPrivateIPOfEc2InstanceContext(modules/aws/ec2.go)按实例 ID 从 AWS 查询私网 IP,演示了「输出拿不到的元数据可以从 AWS API 补齐」;
  • 构造 publicHost 与 privateHost 两个ssh.Host,调用ssh.CheckPrivateSSHConnectionContextE(modules/ssh/ssh.go)—— 该方法先连接公网主机,再通过该连接拨号到私网主机(JumpHost机制)执行命令并返回输出。

validate:SSH Agent 与 SCP 验证

testSSHAgentToPublicHost与testSSHAgentToPrivateHost(test/terraform_ssh_example_test.go)演示了在测试进程内模拟 SSH Agent:通过ssh.SSHAgentWithKeyPair(modules/ssh/agent.go)创建内存中的 Agent(监听 Unix socket,把密钥加入 keyring),并把OverrideSshAgent设进Host,从而验证「不直接使用私钥文件、改走 agent 转发」的场景。

testSCPToPublicHost(test/terraform_ssh_example_test.go)演示文件传输验证:先用ssh.SCPFileToContextE(modules/ssh/ssh.go)把内容Hello, World以0644权限上传到/tmp/test.txt,再用ssh.FetchContentsOfFileContextE(内部执行cat,modules/ssh/ssh.go)读取并比对内容。这组用例说明 Terratest 的 SSH 模块不只是「执行命令」,还覆盖了文件上传/下载类验证。

teardown:安全清理

teardown 阶段(test/terraform_ssh_example_test.go)执行terraform.DestroyContext(modules/terraform/destroy.go)销毁全部资源,再调用aws.DeleteEC2KeyPairContext(modules/aws/keypair.go)删除测试期间导入的 Key Pair,保证账号内不遗留测试残留。

运行自动化测试

README 给出了运行该测试的步骤:

  1. 注册 AWS 账号;
  2. 配置 AWS 凭证(AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY,profile 场景下导出AWS_SDK_LOAD_CONFIG为"True");
  3. 安装 Terraform 并加入PATH;
  4. 安装 Golang,并确保本仓库代码已 checkout 到本地;
  5. cd test;
  6. dep ensure(README 沿用早期 dep 时代的写法;当前仓库已迁移到 Go Modules,仓库根目录维护有 go.mod 与 go.work,可直接在仓库根执行go mod download或go work sync等现代依赖管理命令);
  7. go test -v -run TestTerraformSshExample。

结合阶段化设计,本地迭代时还可以通过环境变量跳过阶段,例如SKIP_teardown=true go test -v -run TestTerraformSshExample,从而保留资源、只反复执行 setup 与 validate。

费用与注意事项

重要提醒(原文 WARNING):该模块及其自动化测试会在你的 AWS 账号中部署真实资源并产生费用。这些资源基本属于 AWS Free Tier 覆盖范围,如果免费额度尚未用完通常无需付费,但所有 AWS 费用完全由你自行负责。测试代码虽然会在 teardown 阶段自动销毁资源并删除 Key Pair,但建议运行前确认账号配置正确、区域选择合理,并在结束后检查是否有残留资源。

延伸阅读

  • 本示例更复杂、更接近生产形态的姊妹示例:terraform-packer-example(Terraform + Packer + Web 服务器端到端);
  • 测试中用到的基础设施校验与生命周期函数,可深入阅读 modules/terraform 与 modules/aws 对应源码;
  • SSH/SCP/Agent 验证的完整 API 列表见 modules/ssh/ssh.go 与 modules/ssh/agent.go;
  • 测试阶段化、临时目录复制与测试数据缓存的实现见 modules/teststructure。
  • 测试
  • 开发工具
  • DevOps
  • 质量保障

【免费下载链接】terratest

Terratest is a Go library that makes it easier to write automated tests for your infrastructure code.

项目地址:https://gitcode.com/gh_mirrors/te/terratest
点击查看免费下载

相关推荐

上一篇:Swift Protobuf 与 Core Data 集成:数据持久化的完美解决方案
下一篇:5分钟部署PP-OCRv6-tiny-det-GGUF:面向新手的OCR模型落地教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询