Terratest の概要
公式HP: https://terratest.gruntwork.io/
Githubリポジトリ: https://github.com/gruntwork-io/terratest
インフラコードに対してテストを書くオープンソースの Go ライブラリで、以下に対応しています。
- Terraform
- Docker
- Packer
- kubernetes
開発元は Gruntwork です。
パッケージと概要
- aws: AWS API を使った機能を提供
・EC2 インスタンスの IP を取得
・リージョンと VPC ID を指定して、含まれているサブネット情報を取得 - collections: Slice や string に対してのいくつかの機能を提供
・指定の Slice に指定の文字列が含まれているかどうかを確認 - docker: docker コマンド、docker-compose コマンドを実行する機能を提供
- files: ファイルやディレクトリに関する機能を提供
・ファイルの存在確認
・ディレクトリの存在確認 - http-helper: http リクエストに関する機能を提供
・http リクエストを送信
・http サーバをローカルに構築 - ssh: サーバに ssh する機能を提供
・ssh 接続後にコマンド実行
・scp でファイルのダウンロード - packer: packer コマンドを実行する機能を提供
- k8s: Kubernetes に関する機能を提供
・Node や Pod、Namespace などを取得
・Pod が立ち上がるまで待機 - Terraform: Terraform コマンドを実行する機能を提供
Terratest の機能
- tfstate に出された output の値を見てアサーション
tfstate を S3 などのオブジェクトストレージで管理している場合でも、追従してくれます。 - リソースの構築・破棄
構築 → テスト → 破棄 のようなテストの流れを行うこともできます。 - http リクエストを送信したりダミーサーバのためのヘルパー
実際に構築したリソースに対してリクエストを送るようなテストも書けます。 - 公式が出している
terratest_log_parser
というツールを使うとテスト結果を JUnit ライクの xml で出力可能
https://github.com/gruntwork-io/terratest/releases
パイプラインに組み込む際はこのツールを挟む使い方になる認識
Terraformにおけるテストのサンプル実行してみた
AWS に EC2 インスタンスを立てて、簡単な HTTP サーバを立てる Terraform のテストです。https://terratest.gruntwork.io/
サンプル Terraform(AWS)
terraform {
# This module is now only being tested with Terraform 0.13.x. However, to make upgrading easier, we are setting
# 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it
# forwards compatible with 0.13.x code.
required_version = ">= 0.12.26"
}
provider "aws" {
region = "us-east-2"
}
# website::tag::1:: Deploy an EC2 Instance.
resource "aws_instance" "example" {
# website::tag::2:: Run an Ubuntu 18.04 AMI on the EC2 instance.
ami = "ami-******"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.instance.id]
# website::tag::3:: When the instance boots, start a web server on port 8080 that responds with "Hello, World!".
user_data = <<EOF
#!/bin/bash
echo "Hello, World!" > index.html
nohup busybox httpd -f -p 8080 &
EOF
}
# website::tag::4:: Allow the instance to receive requests on port 8080.
resource "aws_security_group" "instance" {
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
# website::tag::5:: Output the instance's public IP address.
output "public_ip" {
value = aws_instance.example.public_ip
}
テストコード
テストは動的なものです。 すなわち、実際に terraform apply
でリソースを作成し、それらに対して想定通りの設定となっているかをテストします。
以下コードにコメントで注釈を入れています。
package test import ( "fmt" "testing" "time" http_helper "github.com/gruntwork-io/terratest/modules/http-helper" "github.com/gruntwork-io/terratest/modules/terraform" ) func TestTerraformAwsHelloWorldExample(t *testing.T) { t.Parallel() terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ // terraformコマンドを実行するディレクトリを指定 TerraformDir: "../examples/terraform-aws-hello-world-example", }) // テスト終了後、リソースを消去する defer terraform.Destroy(t, terraformOptions) // init&applyを実行してリソースを作成する terraform.InitAndApply(t, terraformOptions) // 指定keyのoutputを取得する publicIp := terraform.Output(t, terraformOptions, "public_ip") // urlにリクエストを送って、ステータスコードとボディをアサーションする url := fmt.Sprintf("http://%s:8080", publicIp) http_helper.HttpGetWithRetry(t, url, nil, 200, "Hello, World!", 30, 5*time.Second) }
テスト実行(抜粋)
実行ログには terraform
コマンドの実行結果も出力されます。tee
コマンドでログをファイル出力していますが、後述する JUnit 形式でのテスト結果出力の際に使用します。
$ go test -v terraform_aws_hello_world_example_test.go | tee test_output.log ... (中略) ... === RUN TestTerraformAwsHelloWorldExample === PAUSE TestTerraformAwsHelloWorldExample === CONT TestTerraformAwsHelloWorldExample ... (中略) ... TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + create TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: Terraform will perform the following actions: TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: # aws_instance.example will be created TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + resource "aws_instance" "example" { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + ami = "ami-******" TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + arn = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + associate_public_ip_address = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + availability_zone = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + cpu_core_count = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + cpu_threads_per_core = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + get_password_data = false TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + host_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + instance_initiated_shutdown_behavior = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + instance_state = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + instance_type = "t2.micro" TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + ipv6_address_count = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + ipv6_addresses = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + key_name = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + outpost_arn = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + password_data = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + placement_group = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + primary_network_interface_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + private_dns = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + private_ip = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + public_dns = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + public_ip = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + secondary_private_ips = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + security_groups = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + source_dest_check = true TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + subnet_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + tags_all = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + tenancy = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + user_data = "93faf098a13b043bb03f57bf2e1ec8960655de4e" TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + vpc_security_group_ids = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + capacity_reservation_specification { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + capacity_reservation_preference = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + capacity_reservation_target { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + capacity_reservation_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + ebs_block_device { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + delete_on_termination = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + device_name = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + encrypted = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + iops = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + kms_key_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + snapshot_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + tags = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + throughput = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + volume_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + volume_size = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + volume_type = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + enclave_options { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + enabled = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + ephemeral_block_device { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + device_name = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + no_device = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + virtual_name = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + metadata_options { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + http_endpoint = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + http_put_response_hop_limit = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + http_tokens = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + network_interface { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + delete_on_termination = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + device_index = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + network_interface_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + root_block_device { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + delete_on_termination = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + device_name = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + encrypted = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + iops = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + kms_key_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + tags = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + throughput = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + volume_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + volume_size = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + volume_type = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: # aws_security_group.instance will be created TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + resource "aws_security_group" "instance" { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + arn = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + description = "Managed by Terraform" TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + egress = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + ingress = [ TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + { TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + cidr_blocks = [ TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + "0.0.0.0/0", TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: ] TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + description = "" TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + from_port = 8080 TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + ipv6_cidr_blocks = [] TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + prefix_list_ids = [] TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + protocol = "tcp" TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + security_groups = [] TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + self = false TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + to_port = 8080 TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: }, TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: ] TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + name = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + name_prefix = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + owner_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + revoke_rules_on_delete = false TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + tags_all = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + vpc_id = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: Plan: 2 to add, 0 to change, 0 to destroy. TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: Changes to Outputs: TestTerraformAwsHelloWorldExample 2021-06-24T11:26:32+09:00 logger.go:66: + public_ip = (known after apply) TestTerraformAwsHelloWorldExample 2021-06-24T11:26:35+09:00 logger.go:66: aws_security_group.instance: Creating... TestTerraformAwsHelloWorldExample 2021-06-24T11:26:42+09:00 logger.go:66: aws_security_group.instance: Creation complete after 6s [id=sg-******] TestTerraformAwsHelloWorldExample 2021-06-24T11:26:42+09:00 logger.go:66: aws_instance.example: Creating... TestTerraformAwsHelloWorldExample 2021-06-24T11:26:52+09:00 logger.go:66: aws_instance.example: Still creating... [10s elapsed] TestTerraformAwsHelloWorldExample 2021-06-24T11:27:02+09:00 logger.go:66: aws_instance.example: Still creating... [20s elapsed] TestTerraformAwsHelloWorldExample 2021-06-24T11:27:12+09:00 logger.go:66: aws_instance.example: Creation complete after 30s [id=i-******] TestTerraformAwsHelloWorldExample 2021-06-24T11:27:12+09:00 logger.go:66: Apply complete! Resources: 2 added, 0 changed, 0 destroyed. TestTerraformAwsHelloWorldExample 2021-06-24T11:27:12+09:00 logger.go:66: Outputs: TestTerraformAwsHelloWorldExample 2021-06-24T11:27:12+09:00 logger.go:66: public_ip = "18.117.228.107" TestTerraformAwsHelloWorldExample 2021-06-24T11:27:12+09:00 retry.go:91: terraform [output -no-color -json public_ip] TestTerraformAwsHelloWorldExample 2021-06-24T11:27:12+09:00 logger.go:66: Running command terraform with args [output -no-color -json public_ip] TestTerraformAwsHelloWorldExample 2021-06-24T11:27:12+09:00 logger.go:66: "18.117.228.107" TestTerraformAwsHelloWorldExample 2021-06-24T11:27:12+09:00 retry.go:91: HTTP GET to URL http://18.117.228.107:8080 TestTerraformAwsHelloWorldExample 2021-06-24T11:27:12+09:00 http_helper.go:32: Making an HTTP GET call to URL http://18.117.228.107:8080 TestTerraformAwsHelloWorldExample 2021-06-24T11:27:15+09:00 retry.go:103: HTTP GET to URL http://18.117.228.107:8080 returned an error: Get "http://18.117.228.107:8080": dial tcp 18.117.228.107:8080: connect: connection refused. Sleeping for 5s and will try again. ... (中略) ... TestTerraformAwsHelloWorldExample 2021-06-24T11:29:20+09:00 retry.go:91: terraform [destroy -auto-approve -input=false -lock=false] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:20+09:00 logger.go:66: Running command terraform with args [destroy -auto-approve -input=false -lock=false] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:24+09:00 logger.go:66: aws_security_group.instance: Refreshing state... [id=sg-******] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:25+09:00 logger.go:66: aws_instance.example: Refreshing state... [id=i-******] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: Note: Objects have changed outside of Terraform TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: Terraform detected the following changes made outside of Terraform since the TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: last "terraform apply": TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: # aws_security_group.instance has been changed TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: ~ resource "aws_security_group" "instance" { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: id = "sg-******" TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: name = "terraform-20210624022635773400000001" TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: + tags = {} TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: # (9 unchanged attributes hidden) TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: # aws_instance.example has been changed TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: ~ resource "aws_instance" "example" { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: id = "i-******" TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: + tags = {} TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: # (29 unchanged attributes hidden) TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: # (5 unchanged blocks hidden) TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: Unless you have made equivalent changes to your configuration, or ignored the TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: relevant attributes using ignore_changes, the following plan may include TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: actions to undo or respond to these changes. ───────────────────────────────────────────────────────────────────────────── TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: Terraform used the selected providers to generate the following execution TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: plan. Resource actions are indicated with the following symbols: TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - destroy TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: Terraform will perform the following actions: TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: # aws_instance.example will be destroyed TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - resource "aws_instance" "example" { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - ami = "ami-******" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - arn = "arn:aws:ec2:us-east-2:******:instance/i-******" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - associate_public_ip_address = true -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - availability_zone = "us-east-2b" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - cpu_core_count = 1 -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - cpu_threads_per_core = 1 -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - disable_api_termination = false -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - ebs_optimized = false -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - get_password_data = false -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - hibernation = false -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - id = "i-******" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - instance_initiated_shutdown_behavior = "stop" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - instance_state = "running" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - instance_type = "t2.micro" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - ipv6_address_count = 0 -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - ipv6_addresses = [] -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - monitoring = false -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - primary_network_interface_id = "eni-******" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - private_dns = "ip-172-31-21-146.us-east-2.compute.internal" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - private_ip = "172.31.21.146" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - public_dns = "ec2-18-117-228-107.us-east-2.compute.amazonaws.com" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - public_ip = "18.117.228.107" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - secondary_private_ips = [] -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - security_groups = [ TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - "terraform-20210624022635773400000001", TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: ] -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - source_dest_check = true -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - subnet_id = "subnet-******" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - tags = {} -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - tags_all = {} -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - tenancy = "default" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - user_data = "93faf098a13b043bb03f57bf2e1ec8960655de4e" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - vpc_security_group_ids = [ TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - "sg-******", TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: ] -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - capacity_reservation_specification { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - capacity_reservation_preference = "open" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - credit_specification { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - cpu_credits = "standard" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - enclave_options { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - enabled = false -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - metadata_options { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - http_endpoint = "enabled" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - http_put_response_hop_limit = 1 -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - http_tokens = "optional" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - root_block_device { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - delete_on_termination = true -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - device_name = "/dev/sda1" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - encrypted = false -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - iops = 100 -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - tags = {} -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - throughput = 0 -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - volume_id = "vol-******" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - volume_size = 8 -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - volume_type = "gp2" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: # aws_security_group.instance will be destroyed TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - resource "aws_security_group" "instance" { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - arn = "arn:aws:ec2:us-east-2:************:security-group/sg-******" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - description = "Managed by Terraform" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - egress = [] -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - id = "sg-******" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - ingress = [ TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - { TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - cidr_blocks = [ TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - "0.0.0.0/0", TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: ] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - description = "" TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - from_port = 8080 TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - ipv6_cidr_blocks = [] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - prefix_list_ids = [] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - protocol = "tcp" TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - security_groups = [] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - self = false TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - to_port = 8080 TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: }, TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: ] -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - name = "terraform-20210624022635773400000001" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - name_prefix = "terraform-" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - owner_id = "************" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - revoke_rules_on_delete = false -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - tags = {} -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - tags_all = {} -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - vpc_id = "vpc-******" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: } TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: Plan: 0 to add, 0 to change, 2 to destroy. TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: Changes to Outputs: TestTerraformAwsHelloWorldExample 2021-06-24T11:29:32+09:00 logger.go:66: - public_ip = "18.117.228.107" -> null TestTerraformAwsHelloWorldExample 2021-06-24T11:29:35+09:00 logger.go:66: aws_instance.example: Destroying... [id=i-******] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:45+09:00 logger.go:66: aws_instance.example: Still destroying... [id=i-******, 10s elapsed] TestTerraformAwsHelloWorldExample 2021-06-24T11:29:55+09:00 logger.go:66: aws_instance.example: Still destroying... [id=i-******, 20s elapsed] TestTerraformAwsHelloWorldExample 2021-06-24T11:30:05+09:00 logger.go:66: aws_instance.example: Still destroying... [id=i-******, 30s elapsed] TestTerraformAwsHelloWorldExample 2021-06-24T11:30:15+09:00 logger.go:66: aws_instance.example: Still destroying... [id=i-******, 40s elapsed] TestTerraformAwsHelloWorldExample 2021-06-24T11:30:25+09:00 logger.go:66: aws_instance.example: Still destroying... [id=i-******, 50s elapsed] TestTerraformAwsHelloWorldExample 2021-06-24T11:30:31+09:00 logger.go:66: aws_instance.example: Destruction complete after 57s TestTerraformAwsHelloWorldExample 2021-06-24T11:30:31+09:00 logger.go:66: aws_security_group.instance: Destroying... [id=sg-******] TestTerraformAwsHelloWorldExample 2021-06-24T11:30:33+09:00 logger.go:66: aws_security_group.instance: Destruction complete after 2s TestTerraformAwsHelloWorldExample 2021-06-24T11:30:34+09:00 logger.go:66: Destroy complete! Resources: 2 destroyed. --- PASS: TestTerraformAwsHelloWorldExample (249.54s) PASS ok command-line-arguments 250.728s
JUnit形式での出力
JUnit 形式 (xml) でテスト結果を出す際は、terratest_log_parser
という CLI ツールを別途使用します。
$ terratest_log_parser --testlog=./test_output.log --outputdir=./test_output [terratest_log_parser] INFO[2021-06-24T11:38:19+09:00] reading from file [terratest_log_parser] INFO[2021-06-24T11:38:19+09:00] Creating directory /Users/k-nagase/workspace/go/src/github.com/gruntwork-io/terratest/test/test_output [terratest_log_parser] INFO[2021-06-24T11:38:26+09:00] Directory /Users/k-nagase/workspace/go/src/github.com/gruntwork-io/terratest/test/test_output already exists [terratest_log_parser] INFO[2021-06-24T11:38:26+09:00] Closing all the files in log writer [terratest_log_parser] INFO[2021-06-24T11:38:26+09:00] Directory /Users/k-nagase/workspace/go/src/github.com/gruntwork-io/terratest/test/test_output already exists
$ cat ./test_output/report.xml
<!--?xml version="1.0" encoding="UTF-8"?-->
<testsuites>
<testsuite tests="1" failures="0" time="250.728" name="command-line-arguments">
<properties>
<property name="go.version" value="go1.16.3"></property>
</properties>
<testcase classname="command-line-arguments" name="TestTerraformAwsHelloWorldExample" time="249.540"></testcase>
</testsuite>
</testsuites>
ベストプラクティス
https://terratest.gruntwork.io/docs/#testing-best-practices
インフラにおけるテストのベストプラクティスやテクニックなどを紹介しています。
Terratest を使ってみた所感
- Terraform スクリプトを書く際に output にテストしたい項目を出す必要があるのが手間
- そもそも Terraform の output 値を見るテストには意味があまり感じられない
- Go のテストコードを書くことができればそれ以上の知識は必要ないため、習得のハードルは高くないと感じた
- 動的テストとなるので、Terraform plan を元に静的解析をしてくれるような機能が欲しい
- 各種パッケージ使ってテストしたい項目について API を叩いたりリクエスト送ったりして期待値と比較する、または、疎通確認を行うのが主な使い方になりそう
- どの環境でもやりそうな基本的なテストスイートを作っておけばその部分に関しては自動化できる