仕事で terratest を試すことになったので、学んだことを書き留めておく。
terratest とは
terratest は gruntworkが作成した インフラくストラクチャのテストツール。名前からするとおそらくterraform のテストを書くために生まれたようだが、Docker、Packer、Kubernetes、helm にも対応しており、Azure, AWS, GCP の主要なクラウドプロバイダにも対応しており、インフラのテストを書くのならこれでカバーできるんじゃね?という雰囲気が漂っている。本当かどうか試してみよう。
- Testing Terraform code
- Testing Packer templates
- Testing Docker images
- Executing commands on servers over SSH
- Working with AWS APIs
- Working with Azure APIs
- Working with GCP APIs
- Working with Kubernetes APIs
- Testing Helm Charts
- Making HTTP requests
- Running shell commands
- And much more
こちらの動画を見ると、全体像が見れてわかりやすい。
インストール
Go のテストのライブラリとして使うようになっているので、特にインストールなどは必要ない。ただし、terraform を使うなら、事前にterraform がインストールしてある必要があり、PATHの上に存在することが前提条件です。
Getting Started
Readmeに書いているGetting Started で雰囲気を見てみる。事前準備として、terratest のコードをクローンしておこう。
$ git clone git@github.com:gruntwork-io/terratest.git
テスト用のサンプルを動かしてみよう。
$ mkidir helloworld
$ cd helloworld
$ mkdir examples
$ mkdir test
examples
には、テスト対象のスクリプトを入れる。クローンしたterratest から一番簡単なサンプルをコピーしておこう。また、test
には、テストコードをコピーする。
$ cp ../terratest/examples/terraform-basic-example/* examples
$ cp ../terratest/test/terraform_basic_example_test.go test
次に、test ディレクトリに移動して、go module
を初期化しておく。ちなみに、Go を持っていない人は、GO: Getting Startedのページからダウンロードしてください。Go Moduleを使うので、1.13 以上にすると良いと思います。私はgo version go1.13.4 linux/amd64
を使っています。
github.com/TsuyoshiUshio/Spike
の部分は、モジュールをイニシャライズするところです。今回のテストのためのプロジェクトがあるなら、そのリポジトリにしてもいいですが、試したいだけでしたら、他のと被らなそうな github.com の架空のリポジトリにしておけばいいと思います。
$ cd test
$ go mod init github.com/TsuyoshiUshio/Spike
あとは普通にテストを実行します。サンプルにある主要なテストクラスを見てみましょう。とてもシンプルで、
テストコード
-
terraform.Options
を設定する -
terraform.InitAndApply
で、Options で定義した内容を variables として渡してterraform init
,terraform apply
を実行する - defer
terraform.Destroy
でテストが終わった後に、terraform destroy
を実行してリソースを消す
という感じです。気になると思うので、terraform.Options
で設定できる内容を見てみましょう。試していませんが、リトライを設定できたり、SSHAgentを設定出来たりととてもいい感じですね!
terraform.Options
type Options struct {
TerraformBinary string // Name of the binary that will be used
TerraformDir string // The path to the folder where the Terraform code is defined.
Vars map[string]interface{} // The vars to pass to Terraform commands using the -var option.
VarFiles []string // The var file paths to pass to Terraform commands using -var-file option.
Targets []string // The target resources to pass to the terraform command with -target
EnvVars map[string]string // Environment variables to set when running Terraform
BackendConfig map[string]interface{} // The vars to pass to the terraform init command for extra configuration for the backend
RetryableTerraformErrors map[string]string // If Terraform apply fails with one of these (transient) errors, retry. The keys are text to look for in the error and the message is what to display to a user if that error is found.
MaxRetries int // Maximum number of times to retry errors matching RetryableTerraformErrors
TimeBetweenRetries time.Duration // The amount of time to wait between retries
Upgrade bool // Whether the -upgrade flag of the terraform init command should be set to true or not
NoColor bool // Whether the -no-color flag will be set for any Terraform command or not
SshAgent *ssh.SshAgent // Overrides local SSH agent with the given in-process agent
NoStderr bool // Disable stderr redirection
}
テストクラス
テストクラスの主要ポイントは解説しています。t.Parrallel()
はテストをパラレル実行するためのメソッドです。ちなみに、terraform に書かれている内容は単純にローカルにファイルを出力するためのものです。
terraform_basic_example_test.go
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
// An example of how to test the simple Terraform module in examples/terraform-basic-example using Terratest.
func TestTerraformBasicExample(t *testing.T) {
t.Parallel()
expectedText := "test"
expectedList := []string{expectedText}
expectedMap := map[string]string{"expected": expectedText}
terraformOptions := &terraform.Options{ /// Point 1. Parameter
// The path to where our Terraform code is located
TerraformDir: "../examples",
// Variables to pass to our Terraform code using -var options
Vars: map[string]interface{}{
"example": expectedText,
// We also can see how lists and maps translate between terratest and terraform.
"example_list": expectedList,
"example_map": expectedMap,
},
// Variables to pass to our Terraform code using -var-file options
VarFiles: []string{"varfile.tfvars"},
// Disable colors in Terraform commands so its easier to parse stdout/stderr
NoColor: true,
}
// At the end of the test, run `terraform destroy` to clean up any resources that were created
defer terraform.Destroy(t, terraformOptions) // Point 2. Destroy
// This will run `terraform init` and `terraform apply` and fail the test if there are any errors
terraform.InitAndApply(t, terraformOptions) // Point 3. Init and Apply
// Run `terraform output` to get the values of output variables
actualTextExample := terraform.Output(t, terraformOptions, "example")
actualTextExample2 := terraform.Output(t, terraformOptions, "example2")
actualExampleList := terraform.OutputList(t, terraformOptions, "example_list")
actualExampleMap := terraform.OutputMap(t, terraformOptions, "example_map")
// Verify we're getting back the outputs we expect
assert.Equal(t, expectedText, actualTextExample)
assert.Equal(t, expectedText, actualTextExample2)
assert.Equal(t, expectedList, actualExampleList)
assert.Equal(t, expectedMap, actualExampleMap)
}
テスト実行
テストの実行は普通の go のコードとかわりません。ちなみに
-v
はテストを実施したときに詳細にレポートするためのフラグで、テストレポートを出したいときは必須です。あと、timeout ですが、通常の go test のタイムアウト時間は10分なので、デプロイ系のコードなら一発で過ぎてしまうので、長めにしています。
$ go test -v -timeout 30m
:
--- PASS: TestTerraformBasicExample (4.31s)
PASS
ok github.com/TsuyoshiUshio/Spike 4.331s
デバッグと CI に必須のバイナリ
先ほどのテストを実施すると、stdoutにたくさんのメッセージがでてきます。これをCIパイプラインで使うとかなると、どうやって、テストレポートを出そうか?と思うかもしれません。そのためのバイナリもこのツールで用意されています。バージョン番号はReleaseで、terratest_log_parser
のバイナリを持ったバージョンの最新を指定すればよいでしょう。下記のコマンドを実行して、バイナリをどこかPATHが通っているところにおいておけばセットアップ環境です。
$ curl --location --silent --fail --show-error -o terratest_log_parser https://github.com/gruntwork-io/terratest/releases/download/v0.23.0/terratest_log_parser_linux_amd64
$ chmod +x terratest_log_parser
あとは、テスト実行の時に、標準出力をファイルに出力しておいて、terratest_log_parser
をかますだけです。
$ go test -timeout 30m | tee test_output.log
$ terratest_log_parser -testlog test_output.log -outputdir test_output
これで、ディレクトリができて、ログ、JUnit 形式のXMLファイル、詳細のログが出力されますので、CIでもテスト結果のレポートを表示するのは簡単そうです。
今回のまとめ
まずは、基本的なところを理解して、実行してみました。とても簡単にできる印象です。CIも最初から考えてあるし、インストールも簡単なのもうれしいです。
次回
次回からは、具体的にクラウドプロバイダーにデプロイするテストや、ベストプラクティスで述べられている内容に関して検証していきたいと思います。