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

Infrastructure as Code

名簿から消した人のファイルがスクリプト側にだけ残った

TT Labで続きを見る

目標

同じ名簿を、命令的なスクリプトとOpenTofuに与えて、名簿が減ったときに2つの方式が何をするかを比較し、所有範囲・アドレス体系(countとfor_each)・movedによる移動まで、手で確認します。

なぜ重要なのか

命令的なスクリプトは「今何をするか」しか知らないので、名簿から外れたものを片付けるには、過去に何を作ったかを別に記憶しておく必要があります。宣言的なツールは、その記憶を状態ファイルとして持っていて、コードから消えたものを削除として計算します。だからといって、何もかも知っているわけではありません。ツールは自分が作ったものしか知らず、同じディレクトリに誰かが手で入れたファイルは、プランに現れません。また、アドレスを位置の番号で取ると、リストの前のほうの1行のせいで、後ろのリソースがすべて置き換えられる事故が起きるので、キーでアドレスを取り、移すときはmovedで状態に知らせます。

ステップ

  1. /root/iac-conv/users.txtにalice、bob、carolを1行に1つずつ書いてください。/root/iac-conv/imperative.sh <명단파일> <출력디렉터리>は、名簿の名前ごとに<출력>/<이름>.txtにuser <이름>を書き、<출력>/registry.logに名前を1行追記します(プレースホルダーは、順に名簿ファイル、出力ディレクトリ、名前、出力です)。このスクリプトを/root/iac-conv/users.txtと/root/iac-conv/impで2回実行してください。
  2. /root/iac-conv/main.tfで、同じusers.txtを読み込んで(空行を除く)、名前ごとにlocal_file.user["<이름>"]をfor_eachで宣言してください。ファイルは${path.module}/decl/<이름>.txt、内容はuser <이름>を1行です(プレースホルダーは名前です)。initとapplyを行ってください。採点ツールは、状態のキーがusers.txtの名前とまったく同じか、プランがきれいかを見ます。
  3. users.txtからcarolを削除してください。命令的なスクリプトを同じ引数でもう1回実行し、宣言側はtofu plan -out=/root/iac-conv/remove.tfplanでプランを保存してから、そのプランを適用してください。採点ツールは、保存されたプランがcarol 1つの削除だけを含んでいるか、decl/carol.txtは消えてimp/carol.txtは残ったかを見ます。
  4. 誰かが手で/root/iac-conv/decl/mallory.txtを作りました(自分で作成してください。内容は何でもかまいません)。tofu plan -detailed-exitcodeが、依然として0であることを確認してください。そのあと、/root/iac-conv/orphans.sh <관리디렉터리> <작업디렉터리>を作成して、管理ディレクトリにあるのに状態が知らないファイルの名前を、1行に1つずつ出力するようにし(プレースホルダーは、順に管理ディレクトリと作業ディレクトリです)、declに対する結果を/root/iac-conv/orphans.txtに保存してください。
  5. /root/iac-conv/count-demo/main.tfに、names変数(デフォルト値["a", "b", "c"])と、countで作ったlocal_file.nを置いてください。ファイルはout/<인덱스>.txtで、内容はその名前1行です(プレースホルダーはインデックスです)。適用したあと、デフォルト値から"a"を削除して、tofu plan -out=/root/iac-conv/count-demo/shift.tfplanで保存だけします(適用しません)。/root/iac-conv/count-demo/shift.txtに、変わるリソースごとに<주소> <update|replace|delete|create>を書き(プレースホルダーはリソースのアドレスです)、最後に、デフォルト値を["a", "b", "c"]に戻しておいてください。
  6. /root/iac-conv/foreach-demo/main.tfに、同じnames変数と、for_each = toset(var.names)で作ったlocal_file.nを置いてください。ファイルはout/<이름>.txtで、内容は名前1行です(プレースホルダーは名前です)。適用したあと、デフォルト値から"a"を削除して、tofu plan -out=/root/iac-conv/foreach-demo/drop.tfplanで保存だけします。/root/iac-conv/foreach-demo/drop.txtに、ステップ5と同じ形式で書き、デフォルト値を再び["a", "b", "c"]に戻しておいてください。
  7. /root/iac-conv/count-demoのlocal_file.nを、名前をキーとして使うfor_eachに変更してください。ただしファイル名(out/0.txt・out/1.txt・out/2.txt)と内容はそのままにして、movedブロック3つで、n[0]→n["a"]、n[1]→n["b"]、n[2]→n["c"]を知らせてください。プランに作成・削除・置き換えが1つもないことを確認してから適用します。採点ツールは、状態のアドレスが名前のキーに変わり、プランがきれいかを見ます。
  8. /root/iac-conv/converge.sh <작업디렉터리> <관리디렉터리>を作成してください(プレースホルダーは、順に作業ディレクトリと管理ディレクトリです)。applyしたあとの2回目のplan -detailed-exitcodeが0でなければ、not-convergedで始まる行と2、orphans.shで見つけた見慣れないファイルがあれば、strayで始まる行(ファイル名を含む)と3、どちらも問題なければconvergedと0、apply・planが失敗したらerrorで始まる行と1で終了します。採点ツールは、/root/iac-convのコピー(mallory.txtがあるので3)と、自分で作った一時ディレクトリで確認します。元のものでは実行しなくてもかまいません。

参考

命令的なスクリプトを2回実行する

/root/iac-conv/users.txtにalice、bob、carolを1行に1つずつ書いてください。/root/iac-conv/imperative.sh <명단파일> <출력디렉터리>は、名簿の名前ごとに<출력>/<이름>.txtにuser <이름>を書き、<출력>/registry.logに名前を1行追記します(プレースホルダーは、順に名簿ファイル、出力ディレクトリ、名前、出力です)。このスクリプトを/root/iac-conv/users.txtと/root/iac-conv/impで2回実行してください。

>は上書きし、>>は追記します。ファイルの内容は、2回実行しても同じですが、registry.logは実行した回数だけ増えます。この違いが、冪等でない命令的なスクリプトの姿です。

同じ名簿を宣言として書く

/root/iac-conv/main.tfで、同じusers.txtを読み込んで(空行を除く)、名前ごとにlocal_file.user["<이름>"]をfor_eachで宣言してください。ファイルは${path.module}/decl/<이름>.txt、内容はuser <이름>を1行です(プレースホルダーは名前です)。initとapplyを行ってください。採点ツールは、状態のキーがusers.txtの名前とまったく同じか、プランがきれいかを見ます。

file()で読み込んで、split・compact・tosetで集合を作ります。for_eachは、集合かマップしか受け取りません。リソースのアドレスの角括弧の中が、インデックスの数字ではなく名前であることを、tofu state listで確認してください。

名簿から1行削除する: 片方だけが片付く

users.txtからcarolを削除してください。命令的なスクリプトを同じ引数でもう1回実行し、宣言側はtofu plan -out=/root/iac-conv/remove.tfplanでプランを保存してから、そのプランを適用してください。採点ツールは、保存されたプランがcarol 1つの削除だけを含んでいるか、decl/carol.txtは消えてimp/carol.txtは残ったかを見ます。

宣言的なツールは、状態に記録されたものとコードの差を計算するので、コードから消えたキーは削除として現れます。命令的なスクリプトは、現在の名簿だけを見て、過去に作ったものを知りません。

所有範囲の外のファイルは、宣言的なツールも知らない

誰かが手で/root/iac-conv/decl/mallory.txtを作りました(自分で作成してください。内容は何でもかまいません)。tofu plan -detailed-exitcodeが、依然として0であることを確認してください。そのあと、/root/iac-conv/orphans.sh <관리디렉터리> <작업디렉터리>を作成して、管理ディレクトリにあるのに状態が知らないファイルの名前を、1行に1つずつ出力するようにし(プレースホルダーは、順に管理ディレクトリと作業ディレクトリです)、declに対する結果を/root/iac-conv/orphans.txtに保存してください。

tofu show -jsonのvaluesに、状態が把握しているリソースと属性があります。local_fileのfilenameと、ディレクトリのファイル一覧を比較してください。宣言的なツールが収束させる範囲は、宣言したリソースだけです。

countのリストの先頭を削除すると、後ろがすべて変わる

/root/iac-conv/count-demo/main.tfに、names変数(デフォルト値["a", "b", "c"])と、countで作ったlocal_file.nを置いてください。ファイルはout/<인덱스>.txtで、内容はその名前1行です(プレースホルダーはインデックスです)。適用したあと、デフォルト値から"a"を削除して、tofu plan -out=/root/iac-conv/count-demo/shift.tfplanで保存だけします(適用しません)。/root/iac-conv/count-demo/shift.txtに、変わるリソースごとに<주소> <update|replace|delete|create>を書き(プレースホルダーはリソースのアドレスです)、最後に、デフォルト値を["a", "b", "c"]に戻しておいてください。

countのアドレスは、位置の番号です。先頭を抜くと、後ろの要素が1つずつ前に詰まって、同じ番号に別の値が入ります。local_fileは、内容が変わると置き換えになります。

for_eachでは、抜けたものだけが抜ける

/root/iac-conv/foreach-demo/main.tfに、同じnames変数と、for_each = toset(var.names)で作ったlocal_file.nを置いてください。ファイルはout/<이름>.txtで、内容は名前1行です(プレースホルダーは名前です)。適用したあと、デフォルト値から"a"を削除して、tofu plan -out=/root/iac-conv/foreach-demo/drop.tfplanで保存だけします。/root/iac-conv/foreach-demo/drop.txtに、ステップ5と同じ形式で書き、デフォルト値を再び["a", "b", "c"]に戻しておいてください。

for_eachのアドレスはキーです。キー"b"と"c"は、リストの位置と無関係にそのままなので、プランに現れません。

countをfor_eachへ、何も削除せずに移す

/root/iac-conv/count-demoのlocal_file.nを、名前をキーとして使うfor_eachに変更してください。ただしファイル名(out/0.txt・out/1.txt・out/2.txt)と内容はそのままにして、movedブロック3つで、n[0]→n["a"]、n[1]→n["b"]、n[2]→n["c"]を知らせてください。プランに作成・削除・置き換えが1つもないことを確認してから適用します。採点ツールは、状態のアドレスが名前のキーに変わり、プランがきれいかを見ます。

movedブロックは、状態のアドレスだけを変えるという約束です。新しいアドレスの設定が、古いオブジェクトと属性まで同じであってはじめて、移動だけが起きます。ファイル名が変わると、移動のあとに置き換えが続きます。名前から位置の番号を得るには、for式でマップを作ります。

適用して、2回目のプランと見慣れないファイルまで確認する収束チェックを作る

/root/iac-conv/converge.sh <작업디렉터리> <관리디렉터리>を作成してください(プレースホルダーは、順に作業ディレクトリと管理ディレクトリです)。applyしたあとの2回目のplan -detailed-exitcodeが0でなければ、not-convergedで始まる行と2、orphans.shで見つけた見慣れないファイルがあれば、strayで始まる行(ファイル名を含む)と3、どちらも問題なければconvergedと0、apply・planが失敗したらerrorで始まる行と1で終了します。採点ツールは、/root/iac-convのコピー(mallory.txtがあるので3)と、自分で作った一時ディレクトリで確認します。元のものでは実行しなくてもかまいません。

冪等性(2回目のプランが空)と収束(自分の領域に、自分が知らないものがない)は、別の問いです。前のステップのorphans.shを、そのまま呼び出してください。2回目のプランが空にならない設定は、timestamp()のような毎回変わる値から生じます。