💨

Terraform providerをメンテナンスしたときの気付き

2022/12/11に公開

Terraform provider deploygate を久々にメンテナンスしたので、そのときの気づきをまとめました。

https://registry.terraform.io/providers/fnaoto/deploygate/latest

最初に公式ドキュメントを読む

久々に修正すると色々と動かない部分がでてきたので、今一度、公式ドキュメントを読むことにしました。

https://developer.hashicorp.com/terraform/registry/providers/docs

とくにGitHubでtagを作成したときのCI/CDが動かなくなっていたので、下記のリポジトリを参考に修正しました。

https://github.com/hashicorp/terraform-provider-scaffolding

ドキュメントは自動生成する

公式では tfplugindocs を使ってドキュメントを生成するようになっていました。
前回は自分で手書きで書いていたので、自動化できてよかったです。

https://github.com/hashicorp/terraform-plugin-docs

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 を入れました。

https://github.com/dnaeon/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のバイナリがあればそれを使ってくれるようになったみたいです。

https://developer.hashicorp.com/terraform/plugin/sdkv2/testing/acceptance-tests

なので、下記のように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

https://docs.deploygate.com/reference/deploygate-api

自作したライブラリ

https://pkg.go.dev/github.com/fnaoto/go_deploygate

エラーレスポンスはすべて表示する

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で遊んでみてください!

https://registry.terraform.io/providers/fnaoto/deploygate

また、使っている中で何かわからないことがあれば、Issueなどに上げて貰えればお答えいたしますので、ご意見・ご感想いただけると幸いです。

Discussion