LoginSignup
5
4

More than 3 years have passed since last update.

terraform providerを作ったときのTips

Last updated at Posted at 2020-12-22

Terraform の provider を試しにつくってみたときのTipsと苦労話をまとめてみました。

前提

今回つくったのは deploygate provider です。
まだまだ、作成途中ですが、アプリへのテスターの招待と削除の機能は実装しました。

これを作るにあたって、 APIクライアントとしてこちらを使わせていただきました。

参考にさせていただいたサイト

Tipsと苦労話

どこから作ればいいのか分からない

基本的には、providerから作って、data、resourceの流れがいいと思います。
なぜ、dataから作るのかというと、以下のように、Read:のパラメータだけを設定すれば、動くのでわりと簡単に動くからです。


func dataSourceAppCollaborator() *schema.Resource {
    return &schema.Resource{
        Read: dataSourceAppCollaboratorRead,

これが、resourceになると、 Read:/Create:/Update:/Delete:のパラメータが必要になり、それぞれにメソッドを作る必要があります。


func resourceAppCollaborator() *schema.Resource {
    return &schema.Resource{
        Read:   resourceAppCollaboratorRead,
        Create: resourceAppCollaboratorCreate,
        Update: resourceAppCollaboratorUpdate,
        Delete: resourceAppCollaboratorDelete,

ちなみに、provider は main.go と provider.go だけあれば動くので、一旦それをつくって make isntall をすればバイナリは作成可能です。 terraform providers schema -json などで providerが使用可能なのを確認できます。

テストが動かない

たとえば、こういうテストが書かれていた場合、 go test ではテストが実行されません。
これは、TF_ACC=1のオプションがつかないと実行されないみたいです。


package deploygate

import (
    "fmt"
    "testing"

    "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
    "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func Test_DataSourceAppCollaborator_basic(t *testing.T) {
    resource.ParallelTest(t, resource.TestCase{
        PreCheck:  func() { Test_DGPreCheck(t) },
        Providers: testDGProviders,
        Steps: []resource.TestStep{
            {
                Config: testDataSourceAppCollaboratorConfig,
                Check: resource.ComposeTestCheckFunc(
                    testDataSourceAppCollaborator("data.deploygate_app_collaborator.current"),
                ),
            },
        },
    })
}

Makefileにも testtestacc が用意されていました。
ちなみに、 testacc は provider のバイナリを使ってテストを実行しているみたいです。
なので、テスト前に install の項目を依存関係に入れておくといいと思います。


test: 
    go test -i $(TEST) || exit 1                                                   
    echo $(TEST) | xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4                    

testacc: install
    TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m  

バイナリが更新されない

make install を実行しても、terraform から provider を呼び出せないことがありました。
terraform provider は ~/.terraform.d/plugins/<OS>_<ARCH> にないと実行できないみたいです。

ちなみに、Makefileはこんな感じだったので、修正しました。

修正前

install: build
    mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
    mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
修正後

install: build
    mkdir -p ~/.terraform.d/plugins/${OS_ARCH}
    mv ${BINARY} ~/.terraform.d/plugins/${OS_ARCH}

schema.TypeSet で設定したstateの値を取得できない

schema.ResourceSchema: を設定することで、 terraform.tfstate にjsonの形式で状態を保存できるのですが、保存した値を取得するときに一工夫必要です。

とくに、 schema.TypeSet で設定した値を取得する場合は以下のようになります。

まず、 usersnamerole から構成されている struct型の構造体リストです。
これをterraformで使える形にキャストすることが必要です。

構造体がリストの場合、 schema.TypeSet を使えば、そのまま Set することが可能です。

設定
            "users": {
                Type:     schema.TypeSet,
                Required: true,
                Elem: &schema.Resource{
                    Schema: map[string]*schema.Schema{
                        "name": {
                            Type:     schema.TypeString,
                            Required: true,
                        },
                        "role": {
                            Type:     schema.TypeInt,
                            Optional: true,
                        },
                    },
                },
            },

GetOk を使って、取得可能かを確認後に、 (*schema.Set).List() で値を取り出します。
listで取り出した値を map[string]insterface{} にキャストして、目的の値を取得するという流れになります。

取得方法

    var usersList string

    if v, ok := d.GetOk("users"); ok {
        for _, element := range v.(*schema.Set).List() {
            elem := element.(map[string]interface{})
            usersList += elem["name"].(string) + ","
        }
    }

最初、以下のようなGRPCのエラーが出て、どこを修正すればいいのか検討がつきませんでした。


var usersList string
usersList = d.Get("users").(string)
エラー
Error: rpc error: code = Unavailable desc = transport is closing        
panic: interface conversion: interface {} is *schema.Set, not string

Configのテストで variables が渡せない

resource.TestStepでConfigを渡してあげて、テストするんですが、variablesに値を渡せず苦労していました。
今回は、 Config側に variables を記述して、環境変数で値を設定してテストしました。


func Test_DataSourceAppCollaborator_basic(t *testing.T) {
    resource.ParallelTest(t, resource.TestCase{
        PreCheck:  func() { Test_DGPreCheck(t) },
        Providers: testDGProviders,
        Steps: []resource.TestStep{
            {
                Config: testDataSourceAppCollaboratorConfig, // ここ
                Check: resource.ComposeTestCheckFunc(
                    testDataSourceAppCollaborator("data.deploygate_app_collaborator.current"),
                ),
            },
        },
    })
}
修正後

const testDataSourceAppCollaboratorConfig = `
data "deploygate_app_collaborator" "current" {
    owner    = var.owner
    platform = var.platform
    app_id   = var.app_id
}

# variablesを追加

variable "platform" {
  type = string
}

variable "app_id" {
  type = string
}

variable "owner" {
  type = string
}
`
.env
# 環境変数で設定
export TF_VAR_app_id=""
export TF_VAR_owner=""
export TF_VAR_platform=""
export TF_VAR_add_user_name=""

terraform 実行時の crash.log にログを残したい

通常時はログは残さなくていいですが、 terraform で crash が発生したときにデバッグ用にログが残れせると嬉しいなと思って、 log.Printf を使ってログを残せるようにしました。

試しにこんな感じで、必ず crash するようにしてみます。

func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
    return func(d *schema.ResourceData) (interface{}, error) {
        log.Printf("[DEBUG] ログ出しますよー: %s", "おけまる水産")
        var err error
        return nil, err

terraform 実行時に TF_LOG=DEBUG を設定します。

$ export TF_LOG=DEBUG
$ terraform init
$ terraform plan
crash.log

2020/12/22 15:16:24 [TRACE] GRPCProvider: Configure
2020-12-22T15:16:24.662+0900 [DEBUG] plugin.terraform-provider-deploygate: 
2020/12/22 15:16:24 [DEBUG] ログ出しますよー: おけまる水産 #わかりやすいように改行してます
2020/12/22 15:16:24 [TRACE] [walkRefresh] Exiting eval tree: provider.deploygate

こんな感じで、ログが出てくれます。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4