tech.guitarrapc.cóm

Technical updates

Pulumi で Stack解析中の例外でリソースが全て消えてしまうのを防ぐ

PulumiはStackの解析をして、現在のステートとの差分でどのような処理をするかpreview / upで表示します。 このため、Stackの解析中 (=コードをビルドして実行してStack生成中) に例外が生じたときにどのようにハンドルされるかは重要です。

今回はユーザー側の誤った記述で、Stack解析に例外が起こっても処理が止まらず実行されたのをメモしておきます。

tl;dr;

  • sdk/dotnetを利用する場合、Program.Mainの返り値の型はintまたはTask<int>にしましょう
  • Top-level statementならreturnを忘れずに、return await Deployment.RunAsync<MyStack>();としましょう

どのような問題なのか

C# でPulumiを記述する場合、多くの場合はStackを継承した自分の定義を用いるでしょう。 pulumi previewpulumi applyでStackを実行中に例外が発生した場合は、pulumi cliはそれを検知して実行を止めなくてはいけません。 pulumiは例外を検知すると、「何もStackの内容を実行せず」例外をターミナルに表示して終了します。

しかしProgram.Mainメソッドの返り値の型をintあるいはTask<int>にしていないと、例外が起こってもpulumi cliは実行を継続しようとします。 この状態で例外が起こると、「例外以降のコードで記述されたリソースに削除マーカーを付与」して差分+例外をターミナルに表示して終了します。

// bad
public static async Task Main(string[] args)
{
    await Deployment.RunAsync<MyStack>();
}

// bad (Top-Level statement)
await Deployment.RunAsync<MyStack>();

Issueが作られておりそちらを見ると詳しくわかります。

github.com

対処法法は前述のとおり、Mainメソッドの返り値をintTask<int>にしましょう。

// ok
public static async Task<int> Main(string[] args)
{
    return await Deployment.RunAsync<MyStack>();
}

// ok (Expression-bodied Method)
public static async Task<int> Main(string[] args) => await Deployment.RunAsync<MyStack>();

// ok (Top-Level statement)
await Deployment.RunAsync<MyStack>();

何がおこったのか

Pulumiは言語のビルドはpulumi cliと切り離されているので、何気に .NET 6でもビルド、実行できたりします。 その際にうかつにも、await Deployment.RunAsync<MyStack>();とreturnを忘れて書いてしまったために、作成してあったリソースの多くが消えるという目にあったのでした。

// missing return!!
await Deployment.RunAsync<MyStack>();

PulumiはGitHub AappあるいはGitHub ActionsでPR差分を表示できるのですが、GitHub Actionsでコメントを書くのを使っていたために、差分が埋もれてしまい気づけなかったという顛末です。

対処はreturnをつけるだけです。 誰かの役に立つと幸いです。

Pulumi に期待すること

例外起こったらEnvironment.ExitCode = -1など適切な終了コードをセットしてほしいです。 現在はそういったことをしていないのを、終了コードをMainメソッドで示さないと死ぬので基盤で対処してほしい...。

github.com