Terraform providerをメンテナンスしたときの気付き
Terraform provider deploygate を久々にメンテナンスしたので、そのときの気づきをまとめました。
最初に公式ドキュメントを読む
久々に修正すると色々と動かない部分がでてきたので、今一度、公式ドキュメントを読むことにしました。
とくにGitHubでtagを作成したときのCI/CDが動かなくなっていたので、下記のリポジトリを参考に修正しました。
ドキュメントは自動生成する
公式では tfplugindocs を使ってドキュメントを生成するようになっていました。
前回は自分で手書きで書いていたので、自動化できてよかったです。
tfplugindocsの設定
tfplugindocsを使う場合は下記のようなコメントをmain.goに記述します。
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
ただし、tfplugindocsのpackageがどこかでimportされていて、go.modに追加されている必要があります。
公式ではtools/tools.go
にimportして使っていました。
//go:build tools
// +build tools
package tools
import (
// document generation
_ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs"
)
ドキュメントを生成するときはgo generate
を使います。
$ go generate ./...
Terraformのサンプルコードをドキュメントに追加する
各resourceやdataのサンプルコードは下記のディレクトリに入れておくと、自動で各docsにExample Usage
として追加してくれます。
examples
├── data-sources
│ ├── `データソース名のディレクトリ (ex. deploygate_organization_member)`
│ │ └── data-source.tf
├── provider
│ └── provider.tf
└── resources
└── `リソース名のディレクトリ (ex. deploygate_organization_member)`
└── resource.tf
Provider用のドキュメントはテンプレートを使う
データソースやリソースの設定はschema.Resource
で行うのですが、そこにはDescription
というフィールドがあって、そこにコメントを書くと自動的に各ドキュメントにコメントを追加してくれます。
ただし、Providerの設定に使うschema.Provider
では、Description
というフィールドが現在は実装されていないようです。
なので、Prodiver用のドキュメントは別途、templates/index.md.tmpl
を作ってやる必要がありました。
index.md.tmpl
では下記のように、markdownのテンプレート内にTerraformのソースコードを埋め込めます。
## Example Usage
{{ tffile "examples/provider/provider.tf" }}
テストにVCRを入れる
CI/CDを使ってテストを作りたかったので go-vcr を入れました。
go-vcrでは、httpクライアントのrequest/responseをjson形式で保存してくれるので、テストの度にAPIサーバへリクエストを行わなくてもよくなります。
これを使うと、CI/CD環境で自動でテストを行うことができ、さらにForkして修正したい人でもDeployGateアカウントを作らずにテストが実行できます。
リクエストヘッダーのAuthorizationは削除する
VCRではリクエストをJsonとしてファイルに保存します。
ここで、APIリクエストで使ったAPI Tokenなどが保存されてしまう危険性があります。
今回はAddSaveFilter
を使って、リクエストを保存するタイミングでAuthorizationのヘッダーを削除するような実装にしました。
また、必要ないデータ(レスポンスヘッダーなど)はなるべく保存しないようにしました。
VCRを停止するタイミングが大事
VCRはhttpクライアントのTransportに入れて使うのですが、VCR停止するまでの間に複数のリクエスト/レスポンスが行われると直前のデータが上書きされて消されしまいます。
なので、VCRを停止してリクエスト/レスポンスを保存するタイミングが重要になります。
今回は、resource.UnitTestの直前のタイミングでhttpクライアントのTransportを保存していき、最後にまとめてVCRを停止していくような実装にしています。
暫定対処なので、もっといい方法があればアドバイスいただけると嬉しいです!
テストで最新のProviderを使う
TF_ACC
のフラグをつけた場合のテスト(acceptance-tests)で、terraformのproviderを使用してテストを行うのですが、最近のTerraformではGOPAHTのbinにProviderのバイナリがあればそれを使ってくれるようになったみたいです。
なので、下記のようにBuildしてInstallするだけで、acceptance-testsが最新のProviderで実行できます。
$ go mod download
$ go build .
$ go install .
# $GOPATH/bin/<providerのバイナリ> を参照してくれる
$ TF_ACC=1 go test -v -cover -timeout 120m ./...
ただ、実際にTerraformコマンドを使うときは.terraformrc
を使う必要がありました。
コマンド実行時に警告がでますが、実際にTerraformコマンドを使用できるので、リリース前の最終確認などに使えます。
$ go mod download
$ go build .
$ go install .
# ビルドしたバイナリを/tmpに移動して.terraformrcから参照する
$ mv terraform-provider-deploygate /tmp
$ cat <<EOF > .terraformrc
provider_installation {
dev_overrides {
"fnaoto/deploygate" = "/tmp"
}
direct {}
}
EOF
# Terraform init は必要ない
$ TF_CLI_CONFIG_FILE=.terraformrc terraform plan
$ TF_CLI_CONFIG_FILE=.terraformrc terraform apply
$ TF_CLI_CONFIG_FILE=.terraformrc terraform destroy
必要なライブラリは自作する
以前は、DeployGateのライブラリがgo.devに公開されていたので、それをForkして使っていたのですが現在では公開されておらず APIドキュメント を見ながら自作しました。
DeployGate API Docs
自作したライブラリ
エラーレスポンスはすべて表示する
APIの仕様は日々変わっていきます。
エラーレスポンスをフィルターしていると、ステータスコードはわかるけど、そのAPIがdeprecatedなのか、リクエストヘッダーが間違っているのかわかりません。
とくにTerraformでは、ライブラリ側でエラーレスポンスにメッセージが無いと、PlanやApplyが失敗している理由がわからず、デバッグすることもできなくなります。
Terraformで使うライブラリを作るときは、ただエラーを返すだけでなく、メッセージも一緒に表示したほうが良いかと思います。
今後、必要な機能はGitHubのIssueに追加しておく
個人でメンテしているOSSでは、一年ごとにまとめて修正するというようなことが良くあります。
いまは実装できないけど、将来的に追加しておきたい機能などはissueに追加しておくと良いかと思います。
deprecatedを使うかどうか
APIの仕様は日々変わっていくんですが、この仕様変更についていかないといけないのでなかなか大変です。
特に仕様変更や機能追加の多いAPIでは、顕著にこの傾向が見られます。
今回、DeployGateのTerraform providerを大幅に修正したのですが、以前の機能をdeprecatedにするかどうかで迷いました。
結局、deprecatedにはせず、Providerのメジャーバージョンを上げることにしました。
terraform registryには以前のバージョンも使える機能があるので、そっちを使って貰えばいいかなという理由です。
やはり個人でメンテするには、機能をバッサリ切ってメンテコストを下げるような動きになってしまいます。
まとめ
DeployGateのTerraform Providerを久々に修正して、いろいろな気付きがありました。
もし、ご興味あるかたはProviderで遊んでみてください!
また、使っている中で何かわからないことがあれば、Issueなどに上げて貰えればお答えいたしますので、ご意見・ご感想いただけると幸いです。
Discussion