terraform test: 細かい挙動
この記事は 3-shake Advent Calendar 2023 19 日目の記事です!
この記事に書いてあること
この記事を含め 3 回に渡って terraform test の機能を紹介します。
- terraform test: 基本機能
- terraform test: 応用機能
- terraform test: 細かい挙動 <- 今ここ
はじめに
前回の記事では、 terraform test の応用的な機能の紹介をしました。
この記事では、 terraform test の挙動について説明します。
terraform test: 細かい挙動
state
terraform test 実行の state は下記のような特徴があります。
- state はメモリ内に保持する
- state は基本的に
.tftest.hcl
ごとに作成される - 2 の例外として、
module
ブロックを使用しているrun
ブロックがある場合、別の state となる。- この場合、
source
が同じrun
ブロックは同じ state となります。
- この場合、
下記のサンプルコードの場合、state の数は 3 となります。
# file_count.tftest.hcl
variables {
bucket = "saikyo-tftest-bucket"
files = {
"file-one.txt": "data/files/file_one.txt"
"file-two.txt": "data/files/file_two.txt"
}
}
provider "aws" {
region = "us-east-1"
}
run "setup" {
# Create the S3 bucket we will use later.
module {
source = "./testing/setup"
}
}
run "execute" {
assert {
condition = length(aws_s3_object.object) == 2
error_message = "wrong number of files"
}
}
run "verify" {
# Load and count the objects created in the "execute" run block.
module {
source = "./testing/loader"
}
assert {
condition = length(data.aws_s3_objects.objects.keys) == 2
error_message = "created the wrong number of s3 objects"
}
}
実行順序
terraform test では、.tftest.hcl
および run
ブロック単位でシリアルに処理が行われます。
- ファイルの実行順序
- ファイル名で昇順に一つずつ実行されます。
- ファイル内の実行順序
- ファイル内の
run
ブロックを上から順に実行していきます。
- ファイル内の
サンプルコードを用いて実行順序の例を示します。
run "setup" {
module {
source = "./testing/setup"
}
}
run "first" {
command = plan
assert {
# In practice we'd do some interesting checks and tests here but the
# assertions aren't important for this example.
}
# ... more assertions ...
}
run "second" {
command = apply
assert {
# In practice we'd do some interesting checks and tests here but the
# assertions aren't important for this example.
}
# ... more assertions ...
}
run "third" {
command = plan
assert {
# In practice we'd do some interesting checks and tests here but the
# assertions aren't important for this example.
}
# ... more assertions ...
}
run "fourth" {
command = apply
assert {
# In practice we'd do some interesting checks and tests here but the
# assertions aren't important for this example.
}
# ... more assertions ...
}
上記のサンプルコードの場合、terraform test を実行すると下記の順序に処理が行われます。
-
run "setup"
の実行 (apply) -
run "first"
の実行 (plan) -
run "second"
の実行 (apply) -
run "third"
の実行 (plan) -
run "fourth"
の実行 (apply)
削除順序
リソースの削除順序は「実行順序」とは異なります。
仕様としては下記のような形となります。
- メインのリソース (
module
ブロックが定義されないリソース) が削除される -
module
ブロックで定義されたリソースが逆順で削除される。
run "setup" {
module {
source = "./testing/setup"
}
}
run "first" {
command = apply
assert {
# In practice we'd do some interesting checks and tests here but the
# assertions aren't important for this example.
}
# ... more assertions ...
}
run "loader" {
module {
source = "./testing/loader"
}
}
上記のサンプルコードの場合、下記の順序で削除処理が行われます。
-
run "first"
の state を destroy -
run "loader"
の state を destroy -
run "setup"
の state を destroy
terraform 1.7 以降の削除順序
terraform 1.6 で terraform test が登場した時点では、削除の順番は上述のように直感的ではありません。
これに対して、terraform 1.7 では削除順序に修正が入る予定です。
上記の issue により、 run
ブロックを逆順で削除する動きに変わります。
先程のサンプルコードの場合、下記の順序で削除処理が行われます。
-
run "loader"
の state を destroy -
run "first"
の state を destroy -
run "setup"
の state を destroy
run
ブロックの構成
ファイル、 この節では上記までの terraform test の挙動を考慮して、ファイルや run
ブロックの構成をどうしたほうが良いと考えているかを記載します。
.tftest.hcl
ファイル
成功 / 失敗やユースケースごとにファイルを分けています。
ユースケースの違いは、 variable の設定によって特定のリソースの作成有無が変わったりするような場合を指します。
上記のようなファイル分割をする理由として terraform test の実行単位が .tftest.hcl
単位となっているためです。
Go 言語だと .go
ファイルに対してテストファイルがあるケースが一般的なようだったので、同様に .tf
ファイルに対して .tftest.hcl
ファイルを作成してみたのですが、terraform test の場合 state 管理が .tftest.hcl
単位となるため適していないと判断しました。
適していないと判断した具体例として、下記のような module をテストするとします。
- リソースごとに
.tf
ファイルを作成するようなファイル構成を取っている。 - resource A (resource_a.tf) と B (resource_b.tf) を作成する。
- variable によって resource A の作成有無を制御している (resource B は毎回作る)
.tf
ファイルの単位で .tftest.hcl
ファイルを作成し、resource A に関するテストを resource_a.tftest.hcl
に定義する構成とした場合、resource A の作成有無のケースは run
ブロックを分けてテストすることになります。
variable によるリソースの作成有無を同じ .tftest.hcl
でテストすると同じ state に対して作成する/しないを切り替えることになり、実際のユースケースとは異なる挙動となります。
run
ブロック
run
ブロックの単位についても、実際のユースケースにおいて plan / apply する単位で分けています。
terraform test の結果の success / fail の数が run
ブロック単位となっていることから、リソースごとなどテスト対象ごとに run
ブロックを分けたくなったのですが、下記の理由からユースケースと異なる分離をしていません。
-
run
ブロックを細かく分けると、同じ state に対する変更の apply を行うため、実際のユースケースと異なる -
run
ブロックを細かく分けると、run
ブロック単位で plan / apply を実行することによりテスト時間が長くなる
以上となります。
この記事では terraform test 独自の挙動について説明しました。
挙動を理解しないと、予期せぬエラーに遭遇したり、テストの時間が長くなってしまったりするので、特に大きな module をテストする際は気をつけたほうが良いです。
ここまで 3 回に渡って terraform test の機能紹介をしてきましたが、terraform test を使う方の役に立てば幸いです。
Discussion