如何使用Terratest测试基础架构即代码

百家 作者:51CTO技术栈 2022-09-18 11:07:46

译者 | 布加迪

审校 | 孙淑娟


手动设置基础架构是费时又费力的过程。这时候我们可以利用基础架构即代码(IaC)工具来自动管理基础架构。IaC自动化可用于任何类型的基础架构:虚拟机和存储等。随着越来越多的基础架构变成代码,有必要为IaC进行单元测试和集成测试。


本文简要讨论了什么是IaC以及测试基础架构代码的意义,然后深入探讨了如何使用Terratest进行IaC测试。


基础架构即代码(IaC)


基础架构即代码是通过代码配置和设置环境的过程,而不是通过GUI手动创建所需的基础架构和支持系统。比如说,配置虚拟机、设置虚拟机并为其创建监控机制。Terraform、Packer和Ansible就是典型的IaC。借助基础架构即代码,您还可以将基础架构跟踪到Git等版本控制系统中,进行模块化和模板化,以便在多个环境和地区重用相同的代码。灾难恢复是从基础架构即代码获得的重要好处之一。有了IaC,您可以尽快在其他地区或环境复制基础架构。


测试基础架构代码


IaC测试可以分为多个阶段:


1.健全性或静态分析

2.单元测试

3.集成测试


  • 健全性或静态分析


这是测试基础架构代码的初始阶段。在静态分析中,我们确保代码有正确的语法。它还有助于确保我们的代码符合行业标准,并遵循最佳实践。Linter属于这一类。几款典型的健全性测试工具包括面向Chef的foodcritic、面向Docker的hadolint和面向Terraform的tflint等。


  • 单元测试


借助单元测试,我们不用实际配置基础架构即可评估代码。比如可以限制容器以便以非root用户身份运行,或者云网络安全组应该只有TCP协议。几个典型的单元测试是面向Terraform的Conftest和面向Chef Cookbooks的Chefspecs。


以非root用户身份执行的Conftest例子:

package main

deny[msg] {

input.kind == "Deployment"

not input.spec.template.spec.securityContext.runAsNonRoot

msg := "Containers must not run as root"

}

左右滑动查看完整代码


  • 集成测试


在集成测试中,我们希望通过将IaC实际部署到所需的环境中对其进行测试。比如说,您部署了一个虚拟机,并在该机器的端口80上托管Nginx服务器。因此,您将在部署之后检查端口80是否在侦听。


以下是使用ServerSpec执行该操作的例子:

describe port(80) do

it { should be_listening }

end

我们在本文中介绍使用Terrratest对基础架构代码进行集成测试。


Terratest是什么?

我们可以用它来做什么?


Terratest是由Gruntwork开发的Go库,可帮助您为使用Terraform或Packer编写的基础架构即代码创建和自动化测试。它为您提供了各种任务所需的函数和模式,比如:


  • 测试Docker镜像、Helm图和Packer模板。

  • 允许与各种云提供商API兼容,比如AWS和Azure。


Terratest为基础架构代码执行健全性和功能测试。有了Terratest,您可以轻松识别当前基础架构代码中的问题并尽快解决问题。我们还可以利用Terratest对基础架构进行合规测试,比如针对通过IaC创建的任何新S3存储桶启用版本控制和加密。


安装Terratest所需的二进制文件


Terratest主要需要Terraform和Go来执行。我们在这篇博文中使用了Terraform版本1.0.0 和Go版本1.17.6进行测试。


  • 安装Terraform


按照Terraform网站的下载部分(https://www.terraform.io/downloads)在您的计算机上安装Terraform,您可以使用软件包管理器或下载二进制文件,并使其在PATH中可用。


安装后,通过运行以下命令验证是否已正确安装:

terraform version

Go & test依赖项安装可以通过以下步骤来完成:


  • 安装Go


您可以使用Linux发行版的软件包管理器来安装Go,或者遵照Go的安装文档(https://go.dev/doc/install)。


  • Go测试需要gcc来执行测试


go test命令可能需要gcc,您可以使用发行版的软件包管理器安装它。比如在CentOS/Amazon Linux 2上,您可以使用yum install -y gcc。


Terratest实战


现在,我们将使用Terratest执行一些集成测试。安装步骤完成后,克隆terratest-sample存储库,开始执行Terratest。我们将先使用Go编写测试并执行测试。


重要的事先说:


1.您的测试文件名称应包含_test,比如sample_test.go。这是Go查找测试文件的方式。

2.您的测试函数名称应以Test开头,其中T大写。比如说,TestFunction没有问题,但testFunction会给出错误“没有要运行的测试”。


  • 设置AWS身份验证配置


我们需要AWS凭证在AWS中设置基础架构,可以使用环境变量或共享凭证文件进行配置。


基础架构的Terraform代码可以在组件的相应文件夹中找到。若是ec2,它位于ec2_instance下,若是API网关,它位于api_gateway文件夹下。Terratest将Terraform的output.tf的输出作为测试的输入。下面这个代码段用于测试我们是否在使用的ec2实例上有相同的ssh密钥。

package terratest
import (
  "testing"
"github.com/stretchr/testify/assert"
"github.com/gruntwork-io/terratest/modules/terraform"
)
func TestEc2SshKey(t *testing.T) {
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../terraform",
})
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
ec2SshKey := terraform.Output(t, terraformOptions, "instance_ssh_key") assert.Equal(t, "terratest", ec2SshKey)
}

左右滑动查看完整代码


我们将把它分成不同的部分以便理解:第一步,我们定义一个名为Terratest的Go软件包,然后我们导入测试执行所需的不同软件包。

package terratest
import (
"testing"
"github.com/stretchr/testify/assert" "github.com/gruntwork-io/terratest/modules/terraform"
)

左右滑动查看完整代码


一旦我们满足了所有的先决条件,将创建一个函数来执行实际测试:

func TestEc2SshKey(t *testing.T) {
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../terraform",
})
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
ec2SshKey := terraform.Output(t, terraformOptions, "instance_ssh_key") assert.Equal(t, "terratest", ec2SshKey)
}

左右滑动查看完整代码


借助以下部分,我们定义了Terratest应该在其中查找Terraform清单文件(即main.tf和output.tf)的目录,以便创建基础架构。

terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../terraform",
})

左右滑动查看完整代码


在Go中,我们使用defer方法来执行清理任务,它应该是terraform destroy。


我们使用下面的代码片段来定义它:

defer terraform.Destroy(t, terraformOptions)

现在我们可以继续实际执行了:


使用terraform.InitAndApply ,我们调用通常用于Terraform执行的Terraform函数terraform init和apply:

terraform.InitAndApply(t, terraformOptions)

如前所述,Terratest查找来自output.tf的输出,寻找变量定义。


在下面的代码片段中,我们从Terraform输出中获取ssh密钥,并与已定义的ssh密钥名称进行匹配:

ec2SshKey := terraform.Output(t, terraformOptions, "instance_ssh_key") assert.Equal(t, "terratest", ec2SshKey)

左右滑动查看完整代码


执行测试


将目录切换到已克隆存储库的位置。进入到测试文件所在的位置。


初始化Go模块,并下载依赖项。请查看Terratest文档的“设置项目”部分以获取更多详细信息。

go mod init ec2_instance
go mod tidy

最后执行测试:

$ go test –v

--- PASS: TestEc2SshKey (98.72s)
PASS
ok command-line-arguments 98.735s


不妨继续使用Terratest


在上一节中,我们使用Terratest执行了一些基本的测试。现在,我们将通过部署一个以Lambda和ALB作为后端的API网关来执行高级测试。


  • 高级功能


API网关的GET请求将由ALB处理,任何方法将由Lambda通过API网关来处理。部署后,我们将对网关部署URL执行HTTP GET请求,并检查它是否返回成功码。


注意:我们在执行中没有使用任何API_KEY进行身份验证,但您应该使用它来再现API Gateway更实际的使用。

Terraform output.tf
output "lb_address" {
value = aws_lb.load-balancer.dns_name
description = "DNS of load balancer"
}
output "api_id" {
description = "REST API id"
value = aws_api_gateway_rest_api.api.id
}
output "deployment_invoke_url" {
description = "Deployment invoke url"
value = "${aws_api_gateway_stage.test.invoke_url}/resource"
}

左右滑动查看完整代码


  • 测试执行的代码片段


在第一个场景中,我们已经解释了基本语法,因此将直接进入测试函数。

func TestApiGateway(t *testing.T) {
//awsRegion := "eu-west-2"
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../",
})
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
stageUrl := terraform.Output(t, terraformOptions,"deployment_invoke_url") time.Sleep(30 * time.Second)
statusCode := DoGetRequest(t, stageUrl)
assert.Equal(t, 200 , statusCode)
}
func DoGetRequest(t terra_test.TestingT, api string) int{
resp, err := http.Get(api)
if err != nil {
log.Fatalln(err)
}
//We Read the response status on the line below.
return resp.StatusCode
}

左右滑动查看完整代码


在上面的代码片段中,我们定义了函数DoGetRequest来运行HTTP GET测试。然后,我们将此函数的输出用作TestApiGateway函数的输入。


  • 测试执行和输出

TestApiGateway 2022-03-01T06:56:18Z logger.go:66: deployment_invoke_url = "https://iuabeqgmj2.execute-api.eu-west-1.amazonaws.com/test/resource" TestApiGateway 2022-03-01T06:56:18Z logger.go:66: lb_address = "my-demo-load-balancer-
376285754.eu-west-1.elb.amazonaws.com"

TestApiGateway 2022-03-01T06:56:18Z retry.go:91: terraform [output -no-color -json deployment_invoke_url]
TestApiGateway 2022-03-01T06:56:18Z logger.go:66: Running command terraform with args [output –
no-color -json deployment_invoke_url]
TestApiGateway 2022-03-01T06:56:19Z logger.go:66: "https://iuabeqgmj2.execute-api.eu-west-
1.amazonaws.com/test/resource"

--- PASS: TestApiGateway (42.34s)
PASS
ok command-line-arguments 42.347s

左右滑动查看完整代码


如您所见,它执行了测试函数TestApiGateway,其中它对API网关的deployment_invoke_url执行了TTP GET测试,并返回了测试状态。


使用Terratest进行Terratest模块的 
可扩展性和合规测试


我们还可以利用Terratest进行合规测试。一些例子包括:


  • 检查是否在您的SQS队列或S3存储桶上启用了加密。

  • 验证您是否为API网关设置了特定的限制。


我们为API网关开发了Terratest检查机制。在该例子中,我们验证是否为您的API网关添加了Authorizer。


目前,Terratest在其AWS模块中没有API网关模块。您可以在Terratest AWS模块目录中找到可用的AWS模块。Docker、Packer或Helm等其他Terratest模块可以在Terratest模块目录中找到。


我们使用Terratest和AWS Go SDK方法为Authorizer创建了自己的测试函数。


结语


企业及其客户希望产品更快速地交付。基础架构即代码加快了基础架构的配置,恰好满足了这个要求。随着越来越多的基础架构变成代码,用户对测试的需求也在增加。我们在本文中讨论了Terratest之类的工具如何帮助您在将代码部署到生产环境之前对其进行验证。我们介绍了Terratest的工作原理,甚至执行了测试用例来表明它是如何完成的。Terratest的优点之一是具有可扩展性,我们可以通过使用本文中提到的模块实现这种可扩展性。


原文链接:
https://www.cncf.io/blog/2022/07/18/testing-your-infrastructure-as-code-using-terratest/


结识技术大咖,提升IT技能
畅谈开发梦想,拓展人脉资源
参与话题讨论,赢取互动好礼
扫码添加小助手,立即加入社群

由于公众号平台改变了推送规则,如果你想多看到我们的文章,记得点一下在看和星标哦~

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接