LoginSignup
75
50

More than 3 years have passed since last update.

とってもイケてる香りのする terratest を試してみる (1)Getting Started

Posted at

仕事で 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でもテスト結果のレポートを表示するのは簡単そうです。

image.png

今回のまとめ

まずは、基本的なところを理解して、実行してみました。とても簡単にできる印象です。CIも最初から考えてあるし、インストールも簡単なのもうれしいです。

次回

次回からは、具体的にクラウドプロバイダーにデプロイするテストや、ベストプラクティスで述べられている内容に関して検証していきたいと思います。

75
50
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
75
50