Diary of a Perpetual Student

Perpetual Student: A person who remains at university far beyond the normal period

IaC 提供の難しさ: API で宣言的な管理を実現できるか?という視点

Mackerel は Terraform Provider を提供していて、監視ルールや通知チャンネル、ダッシュボードなどの設定が Terraform で記述・管理できるようになっています。

今回は Terraform Provider 提供者の目線から、サービスを IaC で管理できるように対応する難しさを紹介します。

公開 API で宣言的な管理を実現できるか?

Mackerel の Terraform Provider は、内部的に公開 API を呼ぶことで設定の管理を実現しています。よって、公開 API を新たに増やしたり改修したりする場合には、IaC による宣言的な構成・設定管理を API を使って行えるかどうかを気にする必要があります。

具体例: ホワイトリストと、ホワイトリストに自動で追加するフラグの両方を IaC で管理する

例として、やや抽象的な表現にはなってしまいますが、こんな機能を考えてみましょう:

  • ユーザーが設定を制御できない(サービス提供者のみが制御できる)ある全体集合 U がある。
  • 何らかのホワイトリスト whitelist がある。whitelist は U の部分集合
  • 集合 U の要素が増えた時、whitelist にも自動で追加するかを決めるフラグ autoAppendToWhitelistFlag がある
  • whitelist 、autoAppendToWhitelistFlag ともに IaC で管理可能

ここで、以下のように blacklist、 autoAppendToBlacklistFlag 両方を以下のようなコードで管理したいとします。

resource "hoge" "poyo" {
  whitelist = ["a", "b", "c"]
  autoAppendToWhitelistFlag = true
}

autoAppendToWhitelistFlag が true の場合、サービス提供者が集合 U に含まれる要素を追加したときに、whitelist の内部的な値が更新されます。ところが、ユーザーが管理しているコードは不変のままです。

その結果、次回ユーザーが apply したときに失敗してしまいます。 失敗した場合には、現在のサービス側の設定と同じ値が whitelist に入るようにコードを書き換える必要が出るでしょう。あるいは、Provider の実装が素朴だと、これまで自動で追加してきた whitelist の値を上書きして吹き飛ばしてしまうことになるかもしれません。

すなわち、autoAppendToWhitelistFlag を true にしつつ、whitelist を宣言的に管理することはできないのです。この 2つの設定は理屈として競合関係にあるだから当たり前、と思うかもしれないですが、ユーザー目線に立つとこのような設定をしたくなる気持ちもわかります。最初にコードを書いた当時の初期値は決めつつ、その後の差分はいい感じに適応してほしい、といったニーズですね。

宣言的な管理を阻む機能を置き換えてみる

この問題を解決する鍵は、autoAppendToWhitelistFlag という機能を、同等の目的を果たせる別の機能に置き換えられないか、という視点です。

このように図として可視化すると、見えていなかった部分集合が見えてきませんか?そうです、blacklist です。whitelist の初期値を設定しつつ autoAppendToWhitelistFlag = true にしたいニーズというのは、実は blacklist を宣言的に設定したかったということなのです。

以下のように whitelist と blacklist のうち片方を指定可能なインタフェースにすれば、両者の場合で、全体集合 U に新しい要素が追加されても宣言したパラメータがその状態のまま維持されます。結果として、値を書き換えたり ignore_changes を設定したりするユーザーの特別な対応は不要です。

resource "hoge" "poyo" {
  # whitelist = ["a", "b", "c"]
  blacklist = ["d", "e"]
}

まとめ

先ほどの例を無理やり一般化すると、ユーザーが明示的に設定した値が暗黙的に変わり得る場合、それを回避できないかまず考えてみましょう、といったところでしょうか。

IaC を提供する場合、一般的な Web サービスとはまた違った視点でもどんな API にするか気を遣う必要がある、ということを伝えたかったのでした。もちろん、Terraform Provider 用の API を別に用意しても良いのですが、可能であれば REST API を使いまわせると開発工数を減らせてお得です。

他にも感じているけど言語化できていないコツや落とし穴もある気がしていて、ちゃんとまとめたらカンファレンスのトークネタになりそうな気がしています。