创建Terraform提供程序时的技巧
我试着创建Terraform的提供程序并总结了一些技巧和困难经验。
只需要一个选项,以下为中文的本地化释义:
假设
这次做的是DeployGate提供商。
虽然还在建设中,但已经实现了邀请和删除应用的测试人员功能。
- https://github.com/fnaoto/terraform-provider-deploygate
在制作这个时,我选择了这个作为 API 客户端使用。
- https://github.com/recruit-mp/go-deploygate
感谢您提供参考的网站。 de .)
-
- https://learn.hashicorp.com/tutorials/terraform/provider-setup
-
- https://github.com/hashicorp/terraform-provider-aws
-
- https://github.com/jmatsu/terraform-provider-slack
-
- https://github.com/wizaplace/terraform-provider-algolia
-
- https://febc-yamamoto.hatenablog.jp/entry/terraform-custom-provider-01
-
- https://febc-yamamoto.hatenablog.jp/entry/terraform-custom-provider-02
- https://medium.com/spaceapetech/creating-a-terraform-provider-part-1-ed12884e06d7
贴士和辛酸谈
不知道要从哪里开始制作
基本上,我认为从提供者开始创建,并且将数据和资源流向是最好的方法。
为什么要从数据开始创建呢?因为只需要设置“Read:”参数,就可以使它工作,所以相对来说很容易操作。
func dataSourceAppCollaborator() *schema.Resource {
return &schema.Resource{
Read: dataSourceAppCollaboratorRead,
当这个变成资源时,需要有Read:/Create:/Update:/Delete:的参数,并且需要为每个参数创建相应的方法。
func resourceAppCollaborator() *schema.Resource {
return &schema.Resource{
Read: resourceAppCollaboratorRead,
Create: resourceAppCollaboratorCreate,
Update: resourceAppCollaboratorUpdate,
Delete: resourceAppCollaboratorDelete,
順便提一下,只要有 main.go 和 provider.go,provider 就可以运行,因此一旦创建它并进行 make install,就可以生成可执行文件。可以使用 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,已经准备了”test”和”testacc”两个选项。
顺便说一下,”testacc”似乎是使用提供者的二进制文件来执行测试。
所以,在测试之前,将”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调用提供商。
看起来terraform提供者必须位于~/.terraform.d/plugins/_才能运行。
- https://www.terraform.io/docs/extend/how-terraform-works.html#discovery
顺便提一下,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.Resource 中的 Schema,可以将状态以 JSON 格式保存在 terraform.tfstate 文件中,但在获取保存的值时需要进行一些额外的操作。
特别是在使用 schema.TypeSet 进行设置的值的情况下,获取方式如下。
首先,”users” 是由名称(name)和角色(role)构成的 struct 类型的结构体列表。需要将其转换为可用于 Terraform 的形式。
如果结构体是一个列表,可以使用 schema.TypeSet将其直接设置为一个集合。
"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]interface{},以获取目标值。
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 测试中无法传递变量。
我曾经尝试在 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
}
`
# 環境変数で設定
export TF_VAR_app_id=""
export TF_VAR_owner=""
export TF_VAR_platform=""
export TF_VAR_add_user_name=""
希望在执行 Terraform 时记录日志到 crash.log。
平时不需要保存日志,但是当使用Terraform时发生崩溃时,为了调试目的,我想保留日志,所以我使用log.Printf函数记录日志。
我试试看这样,让它一定会崩溃。
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
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
以这样的方式,日志会被生成。