自定义Terraform-Providers(设置和实现读取)-02
一 前言
在这些教程中,您将使用Terraform插件SDKv2,根据一个名为HashiCups的虚构咖啡店应用程序的API编写一个自定义提供程序。通过这个过程,您将了解如何创建数据源、向HashiCups客户机验证提供程序以及创建具有CRUD功能的资源。
创建自定义Terraform提供程序有几个可能的原因,包括:
- 一种内部私有云,其功能要么是专有的,要么对开源社区没有好处。
- 扩展现有提供程序的功能(bug修复、新特性或自定义)
二 预置条件
2.1 应用环境
- 设置开发环境:您将克隆HashiCups存储库并签出样板分支。这包含一个通用Terraform提供程序的脚手架。
- 定义coffees数据源:您将添加一个脚手架,该脚手架定义一个空模式和函数来检索咖啡列表。
- 定义coffees schema:该模式定义了允许Terraform识别、引用和存储coffees数据资源的属性。
- 实现读取:这个read函数调用对/coffees端点的GET请求,然后将其值映射到上面定义的模式。
- 将coffees数据源添加到提供程序架构中:这允许您在配置中使用数据源。
2.2 开发环境
- 安装并配置了Golang 1.15+。
- 本地安装的Terraform 0.14+CLI。
- Docker和Docker组合在本地运行HashiCups实例。
三 设置开发环境
克隆Terraform HashiCups提供程序存储库的样板分支。这可以作为提供者工作区的样板。
git clone --branch boilerplate https://github.com/hashicorp/terraform-provider-hashicups
cd terraform-provider-hashicups
cd docker_compose && docker-compose up -d
curl localhost:19090/health
如果您在本教程中遇到任何困难,请参考implement-read分支以查看本教程中实现的更改。
四 进行开发
4.1 代码结构
样板包括以下内容:
- Makefile包含用于构建、打包和安装HashiCups提供程序的帮助器函数。
它目前是为MacOS Terraform提供程序开发而编写的,但是您可以更改文件顶部的变量以匹配您的OS_ARCH。如果您正在使用Windows,请更新Makefile。您可以在这里找到受支持的GO_ARCH的完整列表。
- BINARY=terraform-provider-${NAME}
+ BINARY=terraform-provider-${NAME}.exe
- OS_ARCH=darwin_amd64
+ OS_ARCH=windows_amd64
install函数被配置为将提供程序安装到默认MacOS和Linux用户插件目录中的适当子目录中,这是由Terraform0.13规范定义的。
-
docker_compose包含初始化HashiCups本地实例所需的文件。
-
example:示例tf文件。
-
hashicups
:包含主提供程序代码。这里将定义提供程序的资源和数据源实现。 -
main.go:是主要入口点。该文件创建Terraform Core可以使用的有效、可执行的Go二进制文件。
4.2 main文件
打开main,进入存储库的根目录。主要功能的内容使用插件SDK的插件库,方便了Terraform内核与插件之间的RPC通信。
package main
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
"terraform-provider-hashicups/hashicups"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: func() *schema.Provider {
return hashicups.Provider()
},
})
}
请注意,ProviderFunc从hashicups包返回一个*schema.provider。
4.3 浏览provider schema
hashiCups/provider.go文件当前定义了一个空提供者。
package hashicups
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
// Provider -
func Provider() *schema.Provider {
return &schema.Provider{
ResourcesMap: map[string]*schema.Resource{},
DataSourcesMap: map[string]*schema.Resource{},
}
}
helper/schema库是Terraform核心的一部分。它抽象了许多复杂性,并确保了提供者之间的一致性。schema.provider类型可以接受:
- 资源支持(
ResourcesMap
andDataSourcesMap
) - 配置key (properties in
*schema.Schema{}
) - 回调配置 (
ConfigureContextFunc
)
您可以使用配置键和回调来对提供程序进行身份验证和配置。您将在向提供程序添加身份验证教程中添加它们。
五 构建Provider
go mod init terraform-provider-hashicups
go mod tidy
make build
./terraform-provider-hashicups
六 构建业务数据源
现在您已经创建了提供者,添加coffees数据资源。咖啡数据源将提取Hashicups提供的所有咖啡的信息。
在hashicups目录中创建一个名为data_source_coffee.go的新文件,并添加以下代码片段。通常,Terraform提供程序将每个数据源放在自己的文件中,该文件以资源命名,前缀为data_source_。
这里导入的库将在DataSourceCoffeeSread中使用。
文件名称:hashicups/data_source_coffee.go
package hashicups
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func dataSourceCoffees() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceCoffeesRead,
Schema: map[string]*schema.Schema{},
}
}
coffees数据源函数返回一个schema.resource,它定义了资源的模式和CRUD操作。由于Terraform数据资源应该只读取信息(而不是创建、更新或删除),因此只定义了read(ReadContext)。
七 定义业务schema
所有Terraform资源都必须有一个架构。这允许提供者将JSON响应映射到模式。
/coffees端点返回coffees数组。下面的示例显示了一个截断的输出。
$ curl localhost:19090/coffees
[
{
"id": 1,
"name": "Packer Spiced Latte",
"teaser": "Packed with goodness to spice up your images",
"description": "",
"price": 350,
"image": "/packer.png",
"ingredients": [
{
"ingredient_id": 1
},
{
"ingredient_id": 2
},
{
"ingredient_id": 4
}
]
},
####
]
由于响应返回一个咖啡列表,coffees模式应该反映这一点。使用以下代码片段更新coffees数据源的架构。
由于响应返回一个咖啡列表,coffees模式应该反映这一点。使用以下代码片段更新coffees数据源的架构。
func dataSourceCoffees() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceCoffeesRead,
Schema: map[string]*schema.Schema{
"coffees": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
},
"price": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"teaser": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"image": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"ingredients": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"ingredient_id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
},
},
},
}
}
注意,coffees模式是coffee(schema.resource)的schema.typelist。
coffee资源的属性应该映射到JSON响应中各自的值。在上面的示例响应中:
咖啡的id是1,一个schema.typeint。
咖啡的名字是“Packer五香拿铁”,一个模式。打字串。
咖啡成分是一个成分对象数组,一个带有元素map[string]*schema.schema{}的schema.typelist。
八 实现Read方法
既然定义了coffees模式,就可以实现dataSourceCoffeesRead函数了。
将下面的read函数添加到您的hashiCups/data_source_coffee.go文件中。
此函数创建对localhost的新GET请求:19090/咖啡。然后,它将响应解码为[]映射[字符串]接口{}。d.Set(“coffees”,coffees)函数将响应主体(coffees对象列表)设置为Terraform coffees数据源,并将每个值分配给其各自的模式位置。最后,它使用SetID设置资源ID。
请注意,此函数返回diag.Diagnostics类型,它可以向Terraform返回多个错误和警告,为用户提供更可靠的错误和警告消息。可以使用diag.FromErr()helper函数将Go错误转换为diag.Diagnostics类型。您将在调试地形提供程序教程中实现这一点。
func dataSourceCoffeesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := &http.Client{Timeout: 10 * time.Second}
var diags diag.Diagnostics
req, err := http.NewRequest("GET", fmt.Sprintf("%s/coffees", "http://localhost:19090"), nil)
if err != nil {
return diag.FromErr(err)
}
r, err := client.Do(req)
if err != nil {
return diag.FromErr(err)
}
defer r.Body.Close()
coffees := make([]map[string]interface{}, 0)
err = json.NewDecoder(r.Body).Decode(&coffees)
if err != nil {
return diag.FromErr(err)
}
if err := d.Set("coffees", coffees); err != nil {
return diag.FromErr(err)
}
d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
return diags
}
非空ID的存在告诉Terraform创建了一个资源。这个ID可以是任何字符串值,但应该是Terraform可以用来再次读取资源的值。由于该数据资源没有唯一的ID,因此将该ID设置为当前UNIX时间,这将强制该资源在每次应用Terraform期间刷新。
当您在Terraform中创建一些内容,但手动删除它时,Terraform应该优雅地处理它。如果API在资源不存在时返回错误,读取函数应该首先检查资源是否可用。如果资源不可用,函数应该将ID设置为空字符串,以便Terraform“销毁”处于状态的资源。下面的代码片段是如何实现这一点的示例;您不需要将此添加到本教程的配置中。
if resourceDoesntExist {
d.SetID("")
return
}
九 在主provider中添加
既然已经定义了数据源,就可以将其添加到提供程序中了。
在hashicups/provider.go文件中,将coffees数据源添加到DataSourcesMap。DataSourcesMap属性接受数据源名称hashicups_coffees和在hashicups/data_source_coffee.go中定义的*schema.resource的映射。资源和数据源名称必须遵循<provider>_<resource_name>约定。
// Provider -
func Provider() *schema.Provider {
return &schema.Provider{
ResourcesMap: map[string]*schema.Resource{},
DataSourcesMap: map[string]*schema.Resource{
"hashicups_coffees": dataSourceCoffees(),
},
}
}
十 测试
go fmt ./...
make install
cd examples
参考链接
- 点赞
- 收藏
- 关注作者
评论(0)