通过Terraform构建的系统的自动化测试
总结
本文介绍了如何有效地测试使用Terraform构建的系统的配置值。具体方法如下:
-
- 将构建系统的配置值记录在外部文件中。
-
- Terraform加载该文件并输入配置值。
- 测试工具也以该文件的值作为预期值进行测试。
通过将构建的输入值作为测试的输入值,可以避免测试输入值和用Terraform构建的输入值的双重管理。
本文中介绍了如何使用Terraform构建和测试GCP系统,构建CICD部署流水线以及执行测试的方法。
此外,本文还介绍了使用InSpec OSS工具CINC进行测试的方法。
志杰 closely observed the background.
通过使用Terraform来构建的系统被认为无需进行配置值测试。然而,使用Terraform的for_each和count等功能,可以实现配置的开关功能。通过这个功能,可以在开发环境、预备环境和生产环境中实现资源的开关。因此,在预备环境中进行测试并通过发布判断后,可以将生产环境的值设为开启,并简单地将其发布到生产环境中,以简化发布流程。
此外,通过使用 Terraform 模块功能,可以隐藏构建系统的逻辑,并能够重用在其他系统中构建的代码。
当我们将Terraform代码复杂化时,尽管操作管理变得简便,但也可能导致所期望的配置无法实现。举个例子,如果涉及到开关功能,在正式发布时可能会忘记将其打开,从而导致发布遗漏。此时,由于Terraform不会报错,我们需要通过目视确认是否存在发布遗漏。因此,我们认为通过Terraform构建的基础架构也需要进行单独的测试。
然而,如果将Terraform的设置值和应该具备的测试代码值分别管理,管理成本将增加两倍。此外,由于忘记更新测试代码中的值,可能会导致执行无意义的测试。因此,可以通过将用于构建Terraform的输入信息作为测试的输入信息来降低管理成本,并避免忘记反映值的情况发生。
本文介绍了将InSpec的输入值作为Terraform的配置值输入的方法。
设计
使用Terraform和InSpec,可以通过在外部文件中管理配置值并在代码中读取来实现在两者中使用相同的输入值。在这里,我们展示了在Terraform和InSpec中同时读取由外部文件管理的值的方法。此外,我们还提供了一个用于管理外部文件、Terraform代码和InSpec代码的目录结构方案。
Terraform的设计
以下将展示使用Terraform来读取外部配置文件并进行设置的方法。配置方法如下:
-
- 将值输入到资源中时,不直接输入,而应从变量中输入。
- 从外部文件中将值分配给局部变量。
由于输入值是测试项目,所以不直接输入,而是将其输入到外部。由于使用变量可能会在发布时进行更改,除非存在特殊情况,例如读取环境变量,否则将通过局部变量传递值。
此外,由于需要与InSpec共享赋值给局部变量的值,因此将其从外部文件中读取。
Terraform函数包括解码yaml、json和csv的函数。
此外,还有从文件路径读取文件的file函数。通过组合这些函数,可以将存储在外部文件中的配置文件作为Terraform的配置文件读取。
我将展示如何在GCP的VPC网络中创建上述内容。
外部配置文件的格式为yaml形式。
将VPC网络配置文件定义为sample.yaml,并将其内容设置为以下内容:
名称:示例
下面是读取上述 sample.yaml 文件并创建 VPC 网络的 Terraform 代码。
locals {
vpc_network = yamldecode(file("./sample.yaml"))
}
resource google_compute_network main {
name = local.vpc_network.name
}
InSpec的设计
由於InSpec是用Ruby編寫的,所以可以通過導入符合外部文件格式的庫並讀取該文件來讀取外部文件的設置值。
以下是使用之前在Terraform设计中介绍的sample.yaml文件进行读取测试的方法:
require `yaml`
require `json`
network = YAML.load_file("./sample.yaml")
describe "sample" do
actual_networks = JSON.parse(command("gcloud compute network list --format json").stdout).map { |vpc| [vpc["name"], vpc]}.to_h
it "vpcネットワーク #{network["name"]}が存在する。" do
expect(actual_networks[network["name"]]["name"]).to eq network["name"]
end
end
目錄設計
需要作为代码进行管理的项目有以下三种类型。
-
- Terraformコード
-
- InSpecのテストコード
- 設定値ファイル
此外,还存在用于CICD的配置文件。
基于以上考虑,我们建议以下目录结构,以便于管理。
在这里,我们将使用GCP的Cloud Build作为CI工具。
.
├── README.md
├── cloudbuild #Cloud Buildのビルド構成ファイル
├── config #設定値ファイル
├── src #Terraformコード
└── test #InSpecコード
以下是一个实施实例。
最后我会给出一个在GCP上创建VPC网络并在该网络上创建GCE的示例实现。下面是一个示意图。 使用Terraform代码,可以使用enabled参数控制GCE的创建:当设为true时会创建,设为false时会删除。本示例演示当设为false时,Terraform不会创建GCE实例,而且此时可以通过InSpec的测试来检测到没有创建。同时,我们将使用Cloud Build来执行该操作。请参考本仓库中的示例代码。
在使用Cloud Build运行示例代码后的结果如下所示。从结果可以看出,由于未创建GCE,因此GCE的测试失败导致出现了错误。

总结
在进行测试以验证使用Terraform代码构建的基础设施时,我们展示了从相同的输入信息进行测试的方法。
尽管在简单使用Terraform时并不需要进行测试,但是在为其添加复杂功能时可能会发生意外情况,因此测试是必要的以避免错误。
通过应用该方法,可以方便地进行测试。
然而,由于需要修改代码以应用输入代码,这会降低每个人的自由度。
此外,测试代码也可能变得复杂,因此在需要时还需要进行测试。