自定义Terraform-Providers(设置和实现读取)-02

举报
kaliarch 发表于 2022/06/11 12:29:32 2022/06/11
【摘要】 一 前言在这些教程中,您将使用Terraform插件SDKv2,根据一个名为HashiCups的虚构咖啡店应用程序的API编写一个自定义提供程序。通过这个过程,您将了解如何创建数据源、向HashiCups客户机验证提供程序以及创建具有CRUD功能的资源。创建自定义Terraform提供程序有几个可能的原因,包括:一种内部私有云,其功能要么是专有的,要么对开源社区没有好处。扩展现有提供程序的...

一 前言

在这些教程中,您将使用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 and DataSourcesMap)
  • 配置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

参考链接

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。