TT Lab
はじめる
学ぶ 学習パス コース

Terraform/OpenTofu基礎

型を書かなかったので誤りが見当違いの行で出た

TT Labで続きを見る

目標

プリミティブ型・コレクション型・構造型を1つずつ宣言して、何が変数の境界で防がれ、何が静かに変換されるかを確認します。オプション属性とデフォルト値、nullを受け取らないという宣言、そして型を書かなかったときにエラーがどこで出るかまで見ます。

なぜ重要なのか

型は、ドキュメントではなく検問所です。型を書いておけば、誤った値が、変数の境界で、tfvarsの行番号とともに防がれ、書かなければ、その値が奥の奥まで流れ込んで、見当違いのメッセージで爆発します。同じミスなのに、直すのにかかる時間が10倍違います。ところが、型の検査は、防ぐだけではなく、変換もします。文字列で書いた数値は数値になり、要素の型を指定したマップに数値を入れると、文字列になります。この変換は、エラーなしに起きるので、あとで比較や算術をしようとする値なら、あらかじめ知っておく必要があります。逆に、構造型は、宣言にない属性を、何も言わずに捨てます。属性名にタイプミスがあると、デフォルト値が適用されたように見えます。何が防がれ、何が変換され、何が消えるか、この3つを区別することが、このモジュールのすべてです。

ステップ

  1. /root/tfb-types/prim/main.tfに、stringの変数name、numberの変数replicas、boolの変数debugと、3つの出力を置いてください。ok.tfvarsには、name = "api"、replicas = "3"、debug = "true"を書いてapplyし、tofu output -jsonを/root/tfb-types/prim.jsonに保存してください。bad.tfvarsには、replicasにリストを入れてplanを実行し、エラーの出力を/root/tfb-types/prim-bad.txtに保存してください。
  2. /root/tfb-types/quiet/main.tfに、map(string)の変数tagsと、list(string)の変数ids、2つの出力を置いてください。v.tfvarsで、tagsにはteam = "core"、rev = 3、tls = trueを、idsには[1, 2, 10]を与えてapplyしたあと、tofu output -jsonを/root/tfb-types/quiet.jsonに保存してください。
  3. /root/tfb-types/coll/main.tfに、list(string)の変数as_list、set(string)の変数as_set、tuple([string, number, bool])の変数as_tupleと、5つの出力(3つの値と、リスト・セットの長さ)を置いてください。v.tfvarsで、最初の2つの変数に同じ["b", "a", "b"]を、タプルに["web", 3, true]を与えてapplyしたあと、tofu output -jsonを/root/tfb-types/coll.jsonに保存してください。続けて、タプルの要素を2つだけ与えたbad.tfvarsでplanを実行して、エラーを/root/tfb-types/tuple-bad.txtに保存してください。
  4. /root/tfb-types/obj/main.tfに、object({ name = string, port = number })の変数svcと、出力を1つ置いてください。ok.tfvarsには、name・portに加えて、宣言にないowner = "team-a"まで書いてapplyし、tofu output -jsonを/root/tfb-types/obj.jsonに保存してください。bad.tfvarsには、nameを抜いてplanを実行して、エラーを/root/tfb-types/obj-bad.txtに保存してください。
  5. /root/tfb-types/opt/main.tfに、svc変数をオブジェクトとして置き、nameだけを必須にして、port(デフォルト8080)、tls(デフォルトfalse)、tags(デフォルトは空のマップ)、aliases(デフォルトは空のリスト)はオプションとして宣言してください。v.tfvarsには、nameだけを与えてapplyしたあと、tofu output -jsonを/root/tfb-types/opt.jsonに保存してください。
  6. /root/tfb-types/nul/main.tfに、nullable = falseの変数を2つ置いてください。デフォルト値"a"があるzoneと、デフォルト値がないmustです。出力も2つ置いてください。ok.tfvarsにzone = null、must = "x"を与えてapplyしたあと、tofu output -jsonを/root/tfb-types/nul.jsonに保存してください。bad.tfvarsにはmust = nullだけを与えてplanを実行して、エラーを/root/tfb-types/nul-bad.txtに保存してください。
  7. /root/tfb-types/late/に、any型の変数looseと、その長さを出力する出力を置き、/root/tfb-types/tight/には、同じ形で、list(string)型の変数tightを置いてください。どちらもv.tfvarsに値7を与えてplanを実行して、出力をそれぞれ/root/tfb-types/late.txtと/root/tfb-types/tight.txtに保存してください。そのあと、2つのエラーの1行目だけを取り出して、/root/tfb-types/blame.txtに、late=<첫 오류 줄>とtight=<첫 오류 줄>の2行で書いてください(Error: の後ろの文言だけです。プレースホルダーは最初のエラー行です)。
  8. /root/tfb-types/schema/main.tfに、service変数を、ステップ5と同じ形(必須のname、オプションのport・tls・tags・aliases)で置いて、その値でservice.confを作成してください。5行は、name=、port=、tls=、tags=(JSONでエンコード)、aliases=(カンマでつなぐ)です。v.tfvarsには、name = "api"、port = "9090"、tags = { team = "core", rev = 3 }、aliases = ["api-1", "api-2"]を与えてapplyしてください。採点ツールが、この設定のコピーに、誤った入力を2種類入れて、拒否されるかを確認します。

参考

プリミティブ型は、変数の境界で引っかかる

/root/tfb-types/prim/main.tfに、stringの変数name、numberの変数replicas、boolの変数debugと、3つの出力を置いてください。ok.tfvarsには、name = "api"、replicas = "3"、debug = "true"を書いてapplyし、tofu output -jsonを/root/tfb-types/prim.jsonに保存してください。bad.tfvarsには、replicasにリストを入れてplanを実行し、エラーの出力を/root/tfb-types/prim-bad.txtに保存してください。

引用符が付いた値でも、型に合わせて変換できれば、ツールが変換してくれます。出力のJSONには、値だけでなく型も一緒に出るので、何として入ったかを、目で確認してください。

コレクションの中では、変換が静かに起きる

/root/tfb-types/quiet/main.tfに、map(string)の変数tagsと、list(string)の変数ids、2つの出力を置いてください。v.tfvarsで、tagsにはteam = "core"、rev = 3、tls = trueを、idsには[1, 2, 10]を与えてapplyしたあと、tofu output -jsonを/root/tfb-types/quiet.jsonに保存してください。

要素の型を指定したコレクションは、入れる値をその型に変換して入れます。数値や真偽が文字列になっても、エラーは出ないので、あとで比較や算術をしようとする値なら、この変換を知っておく必要があります。

同じ入力を、リスト・セット・タプルで受け取ると、違う形で残る

/root/tfb-types/coll/main.tfに、list(string)の変数as_list、set(string)の変数as_set、tuple([string, number, bool])の変数as_tupleと、5つの出力(3つの値と、リスト・セットの長さ)を置いてください。v.tfvarsで、最初の2つの変数に同じ["b", "a", "b"]を、タプルに["web", 3, true]を与えてapplyしたあと、tofu output -jsonを/root/tfb-types/coll.jsonに保存してください。続けて、タプルの要素を2つだけ与えたbad.tfvarsでplanを実行して、エラーを/root/tfb-types/tuple-bad.txtに保存してください。

リストは、書いた順序と重複をそのまま守り、セットは、重複を捨てて、自分の規則で並べ替えます。タプルは、場所ごとに型が決まった固定長なので、個数が違えば、その場で防がれます。

オブジェクトは、足りなければ防ぎ、余れば静かに捨てる

/root/tfb-types/obj/main.tfに、object({ name = string, port = number })の変数svcと、出力を1つ置いてください。ok.tfvarsには、name・portに加えて、宣言にないowner = "team-a"まで書いてapplyし、tofu output -jsonを/root/tfb-types/obj.jsonに保存してください。bad.tfvarsには、nameを抜いてplanを実行して、エラーを/root/tfb-types/obj-bad.txtに保存してください。

オブジェクト型は、宣言した属性だけを受け入れ、宣言にない属性は、エラーではなく切り捨てです。そのため、タイプミスのある属性名は、何も言わずに消えて、デフォルト値が適用されたように見えます。

オプション属性とデフォルト値で、短い入力を受け取る

/root/tfb-types/opt/main.tfに、svc変数をオブジェクトとして置き、nameだけを必須にして、port(デフォルト8080)、tls(デフォルトfalse)、tags(デフォルトは空のマップ)、aliases(デフォルトは空のリスト)はオプションとして宣言してください。v.tfvarsには、nameだけを与えてapplyしたあと、tofu output -jsonを/root/tfb-types/opt.jsonに保存してください。

オプション属性は、2つ目の引数としてデフォルト値を受け取ります。デフォルト値を与えなければ、その属性はnullになり、下流で改めて確認する必要があるので、デフォルト値を一緒に書くほうが、使う側が楽になります。

nullを受け取らないという宣言は、2つの場合に分かれて動作する

/root/tfb-types/nul/main.tfに、nullable = falseの変数を2つ置いてください。デフォルト値"a"があるzoneと、デフォルト値がないmustです。出力も2つ置いてください。ok.tfvarsにzone = null、must = "x"を与えてapplyしたあと、tofu output -jsonを/root/tfb-types/nul.jsonに保存してください。bad.tfvarsにはmust = nullだけを与えてplanを実行して、エラーを/root/tfb-types/nul-bad.txtに保存してください。

nullを受け取らないと宣言したとき、デフォルト値があれば、nullはデフォルト値に置き換わり、デフォルト値がなければ、エラーになります。値がないときに何になるかを、1か所で固定しておく仕組みです。

anyで受け取ると、エラーが奥の奥で出る

/root/tfb-types/late/に、any型の変数looseと、その長さを出力する出力を置き、/root/tfb-types/tight/には、同じ形で、list(string)型の変数tightを置いてください。どちらもv.tfvarsに値7を与えてplanを実行して、出力をそれぞれ/root/tfb-types/late.txtと/root/tfb-types/tight.txtに保存してください。そのあと、2つのエラーの1行目だけを取り出して、/root/tfb-types/blame.txtに、late=<첫 오류 줄>とtight=<첫 오류 줄>の2行で書いてください(Error: の後ろの文言だけです。プレースホルダーは最初のエラー行です)。

型を書いておかないと、誤った値が変数の境界をそのまま通過します。すると、エラーはその値を使う場所で出て、メッセージは変数ではなく、関数の話をします。型を狭くすれば、同じミスが、tfvarsの行番号とともに手前で防がれます。

入れ子の型で、入力の規格を固定する

/root/tfb-types/schema/main.tfに、service変数を、ステップ5と同じ形(必須のname、オプションのport・tls・tags・aliases)で置いて、その値でservice.confを作成してください。5行は、name=、port=、tls=、tags=(JSONでエンコード)、aliases=(カンマでつなぐ)です。v.tfvarsには、name = "api"、port = "9090"、tags = { team = "core", rev = 3 }、aliases = ["api-1", "api-2"]を与えてapplyしてください。採点ツールが、この設定のコピーに、誤った入力を2種類入れて、拒否されるかを確認します。

型をきちんと書いておけば、誤った入力が、tfvarsの行番号とともに、その場で防がれます。文字列で書いたポートが数値として入ることと、マップの中の数値が文字列になること、2つの変換が1つの入力で一緒に起きるので、結果を目で確認してください。