【Terraform】Google Cloud プロバイダーに貢献したい人のための Magic Modules 入門
ご挨拶
こんにちは、クラウドエース株式会社 第一開発部の谷口友哉です。
本記事では、Magic Modules という Google の OSS について取り上げます。
本記事について
皆さんは、Terraform で Google Cloud の環境を構築している時にプロバイダーのバグに遭遇したことはあるでしょうか。私は何度か経験があり、その原因は様々ですが、時にはプロバイダー側の実装が誤っていたことが判明することもあります。
大抵はプロバイダーのバージョンを上げれば修正されるのですが、あまりにもマイナーな機能だと Issue で過去に報告されたまま、ということもあります。
とはいえ、記事執筆時点でこれだけの Open Issue が報告されている状況で、新しい機能への対応など優先度の具合によっては、自分の欲しい機能がサポートされないのは仕方がありません。
どうしても必要な機能なのであれば、自ら PR を投げて貢献しましょう。
この記事では Google Cloud の Terraform プロバイダーに直接貢献したい、けど差し当たって何をすべきかよくわからない方に向けて、コードベースである Magic Modules について、公式が公開しているガイドを紹介しつつ解説します。
本記事で取り上げること:
- Magic Modules の概要
- Google Cloud API の概要
- 貢献プロセスのガイドウェイ
取り上げないこと:
- Go 言語などの一般的な用語知識や詳細な実装方法
補足事項
Magic Modules 自体は 2018年ごろから歴史があるリポジトリのため、それらすべてを紹介することは出来ません。
あくまで「「Maigc Modules 貢献ガイド」のガイド(記事執筆時点版)」ということでご容赦ください。
また、これから Magic Modules に貢献する方は公式ガイドの最新情報も併せてご確認ください。
Magic Modules の概要
Magic Modules とは、Google が開発・公開している OSS で、Google Cloud の Terraform プロバイダーは Magic Modules にあるコードを基に生成されています。
そのため、プロバイダーのリポジトリに直接 PR を投げてもマージしてくれません。ダメージの通る本体は別ということです。
Magic Modules は Terraform プロバイダー等の開発に欠かせない様々な機能を持つリポジトリですが、主に 3つの役割があります。
- Terraform プロバイダーやライブラリなどの単一コードベース
- テンプレート エンジンによるコードの自動生成
- CI/CD パイプラインに組み込まれたテスト
プロバイダーの単一コードベース
Magic Modules を使ってプロバイダーのコードを生成しているのは、開発効率の向上が目的です。
例えば、現在の Terraform プロバイダーは安定版(GA)と Beta 版の 2つが存在しています。
Google Cloud の Terraform プロバイダー リポジトリ:
リポジトリ名 | 説明 |
---|---|
terraform-provider-google | 安定版 |
terraform-provider-google-beta | Beta 版 |
2つのバージョンに分かれているのは、主に Beta 版にしかない新機能をユーザーが使うかどうか選択出来るようにしているためです。
一方で、2つのバージョンを個別に開発してしまうと、大部分は同じコードのリポジトリを 2つ並行して開発する必要があるため修正や機能追加の効率化を図りつつ、両方のプロバイダーを同時に保守するために Magic Modules という単一のコードベースが存在しています。
プロバイダーの開発者は、新機能や不具合修正などはすべて Magic Modules に対して実装し、実装された内容に基づいて Magic Modules が各プロバイダーのコード(ダウンストリーム)を自動的に生成します。
Beta 版でしか利用出来ない機能は、コード上の制御構文によって Beta 版プロバイダーにのみ生成されます。
ちなみに、プロバイダーの他にも Magic Modules のダウンストリームに(terraform-google-conversion)があります。
こちらは、Cloud Asset Inventory で Terraform のコードをエクスポートする機能に利用されているようです。
テンプレート エンジンによるコードの自動生成
Magic Modules では、プロバイダーと同様に Terraform リソースの定義、ドキュメント、テスト等のコードをリソースごとに実装します。
実装の際、Magic Modules 独自のテンプレート エンジンとして MMv1 が利用出来ます。
MMv1 を利用すれば、プロバイダーに必要なコード、ドキュメント、テストのベースを YAML ファイルを定義するだけで生成できます。
場合によっては Go のコードを一切書かなくてもリソースを実装出来ますが、リソースによっては MMv1 のテンプレートでは不足があったり、複雑な実装が必要なケースもあるため、実装内容に合わせて複数のオプションが用意されています。
オプション | 手法 |
---|---|
1. すべてテンプレートで実装可能 | MMv1(YAML) |
2. テンプレートを使いつつ、リソース独自の処理が必要 | MMv1(YAML) + カスタムコード(Go) |
3. テンプレートが使用出来ず、すべて独自の処理が必要 | Handwrite(Go) |
カスタムコードは、MMv1 で生成されるコードにスニペットを挿入する機能で、テンプレートを利用しつつリソース独自の処理をパッチワーク的に実装出来ます。
また、より柔軟な手法としてすべて手書き(Handwrite)する方法も用意されています。
MMv1 によってほとんどのリソースがテンプレート化の恩恵を受けているため、開発者が Google Cloud API の設計を理解していれば比較的簡単にコードを変更したり、新しいリソース定義をスクラッチ出来るようになっています。
CI/CD パイプラインへの組み込み
Google Cloud 本体で新しいリソースや機能がリリースされれば Terraform プロバイダー側も迅速に対応が必要です。
テンプレート化によって迅速に開発が進められるようになったので、今度は Magic Modules へ反映した変更を 2つの Terraform プロバイダー(google
、google-beta
)にも迅速にリリースする必要があります。
そのための仕組みとして Magic Modules と関連するすべてのリポジトリへの反映を含めた一気通貫の CI/CD パイプラインが構成されています。
Magic Modules で PR を作成すると、実行される自動テストの結果が分かりやすく出力されるほか、マージされた後の一連の作業など、あらゆるワークフローが CI ツール(The Magician と呼ばれています)によって自動化されています。
-
ダウンストリームの自動生成と差分チェックレポート
テンプレート エンジンによって 2つの Terraform プロバイダー(google
、google-beta
)の動作に必要なコードを生成し、PR で発生した変更点を評価した情報をレポートします
-
テストの自動実行とレポートの作成
自動生成されたコードは CI/CD パイプラインを通じて Cloud Build などと連携してテストが実行され、実行結果を PR 内でレポートします
-
プロバイダーへの反映
テストが成功して PR が Approve されると、ダウンストリーム側のリポジトリで自動的に PR を作成してマージされます
なお、プロバイダーにマージされた変更は Googler によってどのバージョンのリリースに含められるか最終決定されるようで、実際にリリースされるまではタイムラグがありますが、概ね 1週間〜程度でリリースされるようです。
Google Cloud API の概要
プロバイダーを実装する際、Google Cloud API のドキュメントを参照しますが、前提として基本的な Google Cloud の API 設計について知っておくと良いでしょう。
Google Cloud では、クラウド上のあらゆるリソースを API によって作られた共通インターフェースを介して管理する機能を提供していますが、実際は 1つの巨大な API ではなく、Cloud Run や Compute Engine などの製品ごとに異なる API としてリリースされています。
一方で、API の設計方針については Google Cloud だけでなく Google 全体で共通となっているので、どの製品の、どの API も、設計は共通化されています。
製品が異なっても API 設計が共通になっているため、MMv1 の仕組みが成立しています。
Google 全体で共通となる API の設計方針については API Improvement Proposals(AIPs) として公開されています。
その中から、特に Google Cloud API に関わりがある内容について紹介します。
- 安定性レベル
- 標準メソッドとカスタムメソッド
- 長時間実行オペレーション
安定性レベル
先述した通り、プロバイダーには安定版と Beta 版が存在していますが、Google Cloud API についても同様に異なるバージョンが存在します。
以下は AlloyDB の提供する API の例で、それぞれ API のエンドポイントが異なっていますが、各製品ごとに異なった文字列を使っている場合があります。
AlloyDB:
- GA: https://alloydb.googleapis.com/v1/
- Beta: https://alloydb.googleapis.com/v1beta/
Magic Modules でも、各製品ごとにどのエンドポイントを呼び出すかを上記の内容から定義していますので、Google Cloud の API ドキュメントを確認する際はどのバージョンの API を見るべきかの指標にしましょう。
標準メソッドとカスタムメソッド
各製品の API で定義されているメソッドについて、基本はリソース(Compute Engine であればディスクやインスタンスなど)に対して様々なメソッドが定義されています。
メソッドの種類としては、以下の 2つに分類されます。
-
標準メソッド
- リソースの CRUD(VM インスタンスの作成、読み取り{list / describe}、更新、削除)
-
カスタムメソッド
- リソース固有の特定の操作(例: VM インスタンスの再起動など)
標準メソッドは Terraform 上でリソース管理する上で必ず呼び出されるメソッドになり、ほとんどのリソースで用意されています。そのため、MMv1 のテンプレートで必ず生成されます。
一方で、カスタムメソッドは標準メソッドに含まれないリソース特有の操作を表すため、MMv1 では自動的に生成出来ません。
また、Terraform プロバイダーでカスタムメソッドを実装するには、リソースの CRUD に合わせて呼び出すよう必要があり、単体で呼び出すことはありません。
そのため、カスタムメソッドの実装には、MMv1 + カスタムコードの実装手法が適しています。
長時間実行オペレーション(LRO)
メソッドを呼び出した時、リソースに対する操作が完了するまでに時間がかかる(クラスタを構築したり、即座に有効化出来ない処理といった)場合は長時間実行オペレーション(Long-running operations、LRO)の仕組みが採用されています。
LRO が採用されているメソッドは、リクエストに対して Operation リソースを返します。
Operation リソースは処理の進行状況に関する情報を持っており、クライアントは初回のレスポンスを迅速に受け取りつつ、Operation リソースに対する別の API から処理の状況を取得出来ます。
LRO によって返される Operation リソースの例:
name: [この Operation リソースの名前]
metadata:
operationType: [INSERT, DELETE, UPDATE など]
progress:
description: [RUNNING, FAILED, SUCCESS など]
percentDone: 100
state: [NOT_STARTED, IN_PROGRESS, FINISHED など]
targetResourceName: [実際に操作しているリソースの名前]
done: true
response: [実行結果など]
Terraform プロバイダーも、LRO を呼び出す際は Operation の処理が完了するまで待機させる処理を実装します。
Terraform のタイムアウト値
LRO を実装する際、Terraform プロバイダーのドキュメントに記載されているタイムアウト値が関係してきます。
ドキュメントに記載された値は、実際に Terraform プロバイダー上でリソースを操作する際に使用されており、適切なタイムアウト値が指定されていなければ LRO の完了を待たずにエラーとなる場合があるので、実装する際は適切値を定義しましょう。
開発環境のセットアップ
実際に初めて Magic Modules の修正に取り掛かるにあたっては、貢献ガイドに沿って開発環境をセットアップしましょう。
ガイドを基にセットアップし、環境が準備出来ている事を確認しましょう。
❯ ./scripts/doctor
Check for go in path...
found!
Check for goimports in path...
found!
Check for git in path...
found!
Check for terraform in path...
found!
Check for make in path...
found!
(余談ですが、2024年から MMv1 の仕組みが Ruby (ERB) から Go (text/template) に移行したことで、Ruby の環境セットアップが不要になりました)
次に、ローカルでダウンストリーム(プロバイダーのコード)を生成出来るよう、こちらの手順も実施しましょう。
これで、以下のコマンドを実行して GA 版 / Beta 版のダウンストリームが生成できれば、実際のプロバイダーに対してどのような差分が生成されるかを確認出来ます。
❯ cd magic-modules
❯ make provider VERSION=ga OUTPUT_PATH="$GOPATH/src/github.com/hashicorp/terraform-provider-google"
❯ make provider VERSION=beta OUTPUT_PATH="$GOPATH/src/github.com/hashicorp/terraform-provider-google-beta"
また、ダウンストリーム側に移動して、生成された差分で構文エラーなど無いかチェックします。
テストの実行(後述しますが、受け入れテストは注意して実行しましょう)やプロバイダーをビルドして実際に動作確認が出来ます。
❯ cd $GOPATH/src/github.com/hashicorp/terraform-provider-google
❯ make test
❯ make lint
❯ make build
リソースに変更を加える
ローカルで開発出来る環境が整ったので、実際に変更を加えて行きます。
リソースは MMv1 (+ カスタムコード) で定義している場合と、Handwrite されている場合で修正方法が異なります。
以下のガイドに沿って対象リソースの修正個所を見つけることが出来ます。
また、MMv1 実装の際は、以下のリファレンスに基づいて値を設定します。
MMv1 カスタムコードの実装例
MMv1 でカスタムメソッドの呼び出しを実装する場合は、以下のカスタムコードに関するガイドを確認します。
以下はあくまで一例ですが、Apigee の NAT アドレス(google_apigee_nat_address
)にカスタムコードを追加した時は次のような PR を作成しました。
簡単にコンテキストを説明すると、Apigee の NAT Address リソースではリソースを作成しただけでは NAT 用の IP アドレスが予約されるのみで、実際に NAT アドレスを使用するには別途有効化が必要です(ドキュメント)。
この有効化(:activate
)がカスタムメソッドになるのですが、Terraform でサポートされる以前はユーザーが REST API を直接呼び出す必要がありました。
このメソッドを Terraform でリソース作成・更新する際、同時にカスタムメソッドを呼び出す事で、ユーザーの便宜を図る事が出来ます。
なお、このメソッドでは LRO が採用されており、レスポンスが Operation になっています。
以上の情報を基に、activate
パラメーターとそれを制御する以下のカスタムコードを実装しました。
-
post_create
-
activate
の状態に応じて:activate
メソッドを呼び出す
-
-
custom_update
-
activate
の状態に応じて:activate
メソッドを呼び出す
-
-
constants
- NatAddress リソースが
RESERVED
になるまで待機する処理を定義
- NatAddress リソースが
-
encoders
- 実際のリソースに存在しない
activate
パラメーターを削除する
- 実際のリソースに存在しない
-
decoders
- 実際のリソースの状態から
activate
パラメーターを作成する
- 実際のリソースの状態から
利用可能なカスタムコードの種類(挿入個所)やコードの具体的な実装方法については、他リソースの実装例などが参考になります。
変更内容のテスト
プロバイダーの動作が変わるような変更を実施した場合は、併せてテストの追加を行います。ガイドに沿ってテストを追加しましょう。
主に追加する必要があるテストは受け入れテスト(VCR テストとも言われます)と呼ばれ、テストケースを実行すると実際の Google Cloud 環境を用いてリソースを構築することになります。
基本的にテストは PR を起票すれば CI 上で自動実行されますが、テスト自体の動作確認のためにローカルでの実行も可能です。
構築先の組織や、プロジェクトに紐づける請求先アカウントは環境変数で固定します。また、実行の際は Terraform プロバイダーと同様に ADC(Application Default Credentials) に設定された認証情報を利用するので参考にしてください。
例えば、特定のテストケースのみを指定して受け入れテストをローカル実行する場合引数で指定します。
❯ cd $GOPATH/src/github.com/hashicorp/terraform-provider-google
❯ make testacc TEST=./google/services/apigee TESTARGS='-run=TestAccApigeeEnvironment_apigeeEnvironmentUpdateTest'
詳細なログが必要な場合は、Terraform プロバイダー同様 TF_LOG
環境変数を利用します。
PR の起票
変更を作成したら、あとは PR を起票してレビューを依頼しましょう。
起票の際、Description にリリースノートに記載する文言のベースの記載が必要になります。
また、初回のみ(Google の OSS に初めて貢献する際) Contributor License Agreements(CLA) に同意する必要があります。案内に従って対応しましょう。
PR を起票することで、CI ツールおよび Cloud Build パイプラインによってダウンストリームとテストの実行結果が生成されます。
PR で実行したテストの詳細はレビュアーとなる Googler のみ閲覧可能な為、テスト自体のデバッグ等が必要な場合はローカルで実行しましょう。
以上が Magic Modules に PR を起票するまでの一連の流れとなります。
おわりに
本記事を通じて、Magic Modules に興味を持ってもらえて実際に貢献する方が増えれば幸いです。
最後に、Googler が 2018年に Magic Modules の生まれた背景や今後について語っている動画の文字起こしがあるので共有します。
Discussion