如何测试Terraform文件



我正在定义Terraform文件中的基础架构。我非常喜欢Terraform,但是我很难弄清楚如何测试。我有awspec,这真的很好,可以通过AWS API进行构建结果进行类似RSPEC的测试。但是,是否有一种方法来进行单元测试,例如terraform plan的结果?其他与Terraform一起使用的工作流是什么样的工作流?

我将在Begin的答案上进行扩展,并提供有关厨房terraform的更多信息。

厨房 - 框架是在test-kitchen中运行的一组开源插件,这些插件应该进入您的Terraform模块存储库中,以测试该模块的功能,然后再在创建资源的存储库中使用。请随时查看这两个项目的文档以获取更多详细信息,但我将仔细研究您的Terraform代码的集成建议。

安装红宝石,Terraform在此示例中,Terraform模块存储库将被称为:my_terraform_module

mkdir -p my_terraform_module
cd my_terraform_module
mkdir -p test/integration/kt_suite/controls 
         test/fixtures/tf_module/

创建Gemfile

source "https://rubygems.org/" do
  gem "kitchen-terraform"
end

安装必要的组件(使用Gemfile用于kitchen-terraform的依赖项)

gem install bundler
bundle install

创建test-kitchen文件.kitchen.yml-这将测试框架,测试 - kitchen和厨房terraform

汇集在一起
---
driver:
  name: terraform
  root_module_directory: test/fixtures/tf_module
  parallelism: 4
provisioner:
  name: terraform
transport:
  name: ssh
verifier:
  name: terraform
  groups:
    - name: basic
      controls:
        - file_check
        - state_file
platforms:
  - name: terraform
suites:
  - name: kt_suite

您的Terraform代码应处于Terraform模块存储库的根源,例如:

my_terraform_module/
  |-- main.tf

可以在main.tf

中使用的示例代码
resource "null_resource" "create_file" {
  provisioner "local-exec" {
    command = "echo 'this is my first test' > foobar"
  }
}

然后,我们像在Terraform Live Repos中一样引用Terraform模块 - 但是在该文件中的测试固定装置中:test/fixtures/tf_module/main.tf

module "kt_test" {
  source = "../../.."
}

然后从那里开始使用Terraform,但是使用厨房特征和测试 - kitchen进行了一些不同的事情,您可以运行收敛性,这有助于跟踪状态和其他几个项目。

bundle exec kitchen converge

现在,您已经看到您的Terraform代码进行了应用,我们需要对其进行测试。我们可以测试创建的实际资源,就像集成测试一样Terraform代码。

创建一个Inspec默认配置文件:test/integration/kt_suite/inspec.yml

---
name: default

为您的集成测试创建一个Inspec控件:test/integration/kt_suite/controls/basic.rb-我正在使用示例的Terraform代码的测试,我之前用于main.tf

# frozen_string_literal: true
control "file_check" do
  describe file('.kitchen/kitchen-terraform/kt-suite-terraform/foobar') do
    it { should exist }
  end
end

这是从状态文件中获取信息并测试是否存在的示例测试。这是一个基本的,但是您绝对可以在此示例上进行。

# frozen_string_literal: true
terraform_state = attribute "terraform_state", {}
control "state_file" do
  describe "the Terraform state file" do
    subject do json(terraform_state).terraform_version end
    it "is accessible" do is_expected.to match /d+.d+.d+/ end
  end
end

然后,使用测试 - kitchen和厨房terraform运行Inspec控件:

bundle exec kitchen verify

我从"入门指南"和这里的一些教程中获取了很多:https://newcontext-oss.github.io/kitchen-terraform/getting_started.html

我们最近开放了瑞士军刀测试基础设施代码的Terratest。

今天,您可能会通过部署,验证和不操纵来手动测试所有基础架构代码。Terratest可以帮助您自动化此过程:

  1. 在Go中写测试。
  2. 使用Terratest中的助手执行您的真实IAC工具(例如Terraform,Packer等),以在真实环境(例如AWS)中部署实际基础结构(例如服务器)。
  3. 使用Terratest中的助手通过做出HTTP请求,API呼叫,SSH连接等,在该环境中验证基础架构是否正常工作。
  4. 使用Terratest中的助手在测试结束时不剥削所有内容。

这是一些Terraform代码的示例测试:

terraformOptions := &terraform.Options {
  // The path to where your Terraform code is located
  TerraformDir: "../examples/terraform-basic-example",
}
// This will run `terraform init` and `terraform apply` and fail the test if there are any errors
terraform.InitAndApply(t, terraformOptions)
// At the end of the test, run `terraform destroy` to clean up any resources that were created
defer terraform.Destroy(t, terraformOptions)
// Run `terraform output` to get the value of an output variable
instanceUrl := terraform.Output(t, terraformOptions, "instance_url")
// Verify that we get back a 200 OK with the expected text
// It can take a minute or so for the Instance to boot up, so retry a few times
expected := "Hello, World"
maxRetries := 15
timeBetweenRetries := 5 * time.Second
http_helper.HttpGetWithRetry(t, instanceUrl, 200, expected, maxRetries, timeBetweenRetries)

这些是集成测试,根据您的测试,可能需要5-50分钟。这不是很快(尽管使用Docker和测试阶段,您可以加快某些的速度),您必须努力使测试可靠,但值得花时间。

查看文档的Terratest回购和许多类型的基础架构代码的示例以及它们的相应测试。

在我的研究中,这是一个棘手的问题,因为Terraform并不是一种完整的编程语言,您正在宣布使用Terraform的资源,而不是如何构建它们,尝试进行单位测试并没有真正为您提供您正在建立资源的保证,而无需实际运行申请。这使试图单位测试对我来说更像是绒毛。

但是,您可以用pyhcl之类的内容来解析HCl文件,或者您可以计划文件,但是根据我的经验,这是很少的好处(但是我可能会缺少一种更轻松的方法!)。

如果您想测试Terraform的结果,则有一些替代方法:

厨房terraform是为您的基础设施编写测试厨房规格的工具。

厨房 - 佛罗里达人 - awspec有助于将awspeckitchen-terraform汇总在一起,尽管我没有亲自使用。

如果您使用的是AWS,我发现AWS配置能够提供与其他基础架构测试工具相同的好处,而没有太多的设置/维护。尽管它是相当新的,但我没有广泛使用。

另外,如果您要支付Terraform Premium,您可以访问Sentinel,这似乎与AWS配置提供了许多类似的好处,但是我没有亲自使用。

除了答案外,我还将加两个美分。我并不很高兴使用Lang与Terratest的Go Lang,尽管它运作良好。只是Go不是我最喜欢的编程语言。我在Java寻找一些框架,发现Terraform-Maven。乍一看,我只在Groovy中找到了示例,但是由于Groovy在JVM上运行,因此可以在Java中实现相同的示例。

我将s3preprovisionspec.groovy的一部分转换为Java。它正在测试此main.tf文件。

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class S3PreProvisionTest {
    private final String TF_CHANGE = "change";
    private final String TF_AFTER = "after";
    private final String TF_TAGS = "tags";
    private final Map<String, String> mandatoryTags = Map.of(
        "application_id", "cna",
        "stack_name", "stacked",
        "created_by", "f.gutierrez@yieldlab.de"
    );
    private Terraform terraform;
    private TfPlan tfplan;
    @BeforeAll
    void setup() {
        terraform = new Terraform().withRootDir("s3_pre_post_demo")
        // .withProperties(Map.of("noColor", "true"))
        ;
        tfplan = terraform.initAndPlan();
    }
    @AfterAll
    void cleanup() {
        terraform.destroy();
    }
    @Test
    void mandatoryTagsForS3Resources() {
        List<Map> s3Bucket = tfplan.getResourcesByType("aws_s3_bucket");
        System.out.println("=========================");
        s3Bucket.forEach(map -> {
            Map tfChangeMap = (Map) map.get(TF_CHANGE);
            Map tfAfterMap = (Map) tfChangeMap.get(TF_AFTER);
            Map tfTagsMap = (Map) tfAfterMap.get(TF_TAGS);
            assertEquals(3, tfTagsMap.size());
            mandatoryTags.forEach((k, v) -> {
                assertEquals(v, tfTagsMap.get(k));
            });
            try {
                JSONObject jsonObject = new JSONObject(map);
                JSONObject jsonChange = jsonObject.getJSONObject(TF_CHANGE);
                JSONObject jsonAfter = jsonChange.getJSONObject(TF_AFTER);
                JSONObject jsonTags = jsonAfter.getJSONObject(TF_TAGS);
                System.out.println(">>>>>>>>>>>>>>>>>>>> " + jsonTags.toString());
                mandatoryTags.forEach((k, v) -> {
                    try {
                        assertEquals(v, jsonTags.getString(k));
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                });
            } catch (JSONException e) {
                e.printStackTrace();
            }
        });
    }
}

一种方法是使用-out=tempfile将结果输出到文件,然后运行脚本以验证您要尝试的任何事情,如果所有通过文件中的文件命令。在这里查看-out:https://www.terraform.io/docs/commands/plan.html

<<div class =" ans">

您可以使用github.com/palantir/tfjson parse .planjson的文件归档。

目前存在一个问题,它给出了"未知计划文件版本:2" 错误。这是因为Terraform的供应商版太旧了。

修复程序是:

go get github.com/palantir/tfjson
cd $GOPATH/src/github.com/palantir/tfjson
rm -rf vendor
go get -v ./...

然后在../../hashicorp/terraform/config/testing.go中存在错误。要修复只需更改行

t.Helper()

to

//t.Helper()

再次运行go get,然后运行go install

go get -v ./...
go install ./...

然后,您应该能够执行以下将产生json输出的以下操作。

terraform plan --out=terraform.plan 
tfjson terraform.plan

最新更新