拓冰建站拓冰建站
首页 / 资讯中心 / 正文

在 Terraform AWS Provider 中新增 Provider-defined Function 的完整开发指南

在 Terraform AWS Provider 中新增 Provider-defined Function 的完整开发指南【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-awsProvider-defined functions提供方自定义函数是 Terraform 1.8 引入的能力允许 Provider 开发者将针对特定云厂商或场景的工具函数直接暴露给 Terraform 配置调用。本指南基于 terraform-provider-aws 仓库中的 add-a-new-function.md 文档结合仓库内skaff脚手架生成器、internal/function下已实现的真实函数以及 Plugin Framework Provider 的注册代码完整讲解从脚手架生成、参数与逻辑实现、Provider 注册、单元测试到注册表文档发布的端到端流程。读完本文你将能够在 AWS Provider 中独立添加一个可被provider::aws::xxx()语法调用的纯函数。什么是 Provider-defined FunctionProvider-defined function 是随 Terraform 1.8 一起引入的 Plugin Framework 特性Provider 开发者可以把与特定 Provider 或特定使用场景绑定的工具函数注册进 Provider用户在配置中通过provider::aws::function_name(...)的形式直接调用。AWS Provider 中的函数提供的是与 AWS 资源配合使用时具有实用价值的工具能力例如本仓库中已实现的 internal/function 包下的四个函数arn_build构造 ARNarn_parse将 ARN 解析为其组成部分trim_iam_role_path裁剪 IAM Role 路径user_agent生成 User-Agent 字符串。纯函数约束为什么函数不能调用 AWS API添加函数的唯一前置条件是确认目标功能适合做成 Provider-defined function。函数必须满足纯函数要求同一输入在每次执行时都产生同一输出可复现。这一要求天然排除了网络调用——凡是需要调用 AWS API 的操作都不应实现为函数而应改用 data source数据源。数据操作类任务字符串处理、解析、拼接等是最常见的函数使用场景。这一约束从仓库现有实现中可以得到印证arn_parse、arn_build等函数全部在本地完成纯计算不发起任何 AWS API 请求执行速度与普通 Go 函数一致。开发流程总览新增一个函数需要经过以下步骤下文逐一展开Fork Provider 并创建功能分支使用skaff function生成脚手架文件填写函数参数与返回值Definition方法实现函数逻辑Run方法将函数注册到 ProviderFunctions方法编写并通过单元测试完善注册表文档提交 Pull Request 并等待优先排序。第一步Fork Provider 并创建功能分支新函数的分支命名规范为f-{function name}例如f-arn_parse。分支命名的详细约定参见 Raising a Pull Request。第二步用 skaff function 生成脚手架仓库提供了skaff工具用于为新函数生成骨架文件。首先安装skaff并进入函数定义目录make skaffcd internal/function随后运行skaff function子命令。-n名称与-d描述两个标志为必填项。名称参数应按 mixed caps 规范书写例如FooBar生成器会自动将其转换为合适的 snake_case 形式skaff function -n Example -d Makes some output from an input.该命令会生成三个文件函数定义文件、单元测试文件、注册表文档文件。生成器源码解析从 skaff/function/function.go 的Create函数可以看到生成器的完整行为名称校验名称必须正确大写例如ARNBuild否则报错name should be properly capitalized若显式传入 snake 名称则必须是全小写下划线形式例如arn_build未传时由names.ToSnakeCase自动转换三个模板文件通过 Goembed嵌入function.gtpl实现、functiontest.gtpl测试、websitedoc.gtpl文档输出文件命名snake_name.go、snake_name_test.go文档写到../../website/docs/functions/snake_name.html.markdown模板数据包含函数名Function、小写前缀名FunctionLower、snake 名FunctionSnake、描述Description以及是否包含提示注释IncludeComments。在 function.gtpl 模板中可以看到生成的 Go 文件骨架遵循固定结构包声明 → 导入 → 函数 struct 与New*初始化函数 →Metadata、Definition、Run三个方法按此顺序。模板还会生成编译期接口校验var _ function.Function exampleFunction{}并默认生成一个参数不等于 foo 则报错的示例逻辑以及fmt.Sprintf(%s-bar, arg)的示例结果供开发者替换。第三步填写函数参数与返回值Definition 方法函数 struct 的Definition方法用于声明预期的参数与返回值。参数名和返回值命名应使用snake_casefunc (f exampleFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { resp.Definition function.Definition{ Parameters: []function.Parameter{ function.StringParameter{Name: some_arg}, }, Return: function.StringReturn{}, } }上述代码定义了一个接受字符串参数some_arg、返回字符串值的函数。框架还支持更丰富的参数与返回类型组合例如 internal/function/arn_parse.go 中真实函数arn_parse的返回类型是一个对象object其属性类型通过map[string]attr.Type声明var arnParseResultAttrTypes map[string]attr.Type{ partition: types.StringType, service: types.StringType, region: types.StringType, account_id: types.StringType, resource: types.StringType, } func (f arnParseFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { resp.Definition function.Definition{ Summary: arn_parse Function, MarkdownDescription: Parses an ARN into its constituent parts, Parameters: []function.Parameter{ function.StringParameter{ Name: arn, MarkdownDescription: ARN (Amazon Resource Name) to parse, }, }, Return: function.ObjectReturn{ AttributeTypes: arnParseResultAttrTypes, }, } }同时函数的Metadata方法中通过resp.Name声明函数在 Terraform 中的调用名即provider::aws::后的部分func (f arnParseFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { resp.Name arn_parse }第四步实现函数逻辑Run 方法函数 struct 的Run方法承载核心逻辑处理入参、设置返回值以及中间的数据处理。模板骨架如下func (f exampleFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { var data string resp.Error function.ConcatFuncErrors(req.Arguments.Get(ctx, data)) if resp.Error ! nil { return } // // Function logic goes here // resp.Error function.ConcatFuncErrors(resp.Result.Set(ctx, data)) }实现要点与 function.gtpl 中的 TIP 注释一致参数获取用req.Arguments.Get(ctx, data)按声明顺序将入参读取到本地变量错误必须通过function.ConcatFuncErrors聚合到resp.Error并提前返回错误处理只要执行了可能返回错误的逻辑就应把resp.Error设为function.ConcatFuncErrors(...)的返回值可用function.NewFuncError构造自定义错误信息结果设置resp.Result.Set(ctx, result)永远是方法最后一步且其返回的错误同样必须处理。arn_parse的真实实现展示了完整模式internal/function/arn_parse.go先从请求参数中取出 ARN 字符串调用 AWS SDK 的arn.Parse解析出错时构造NewFuncError返回解析成功后把 partition、service、region、account_id、resource 五个字段组装进types.ObjectValue再写入结果func (f arnParseFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { var arg string resp.Error function.ConcatFuncErrors(req.Arguments.Get(ctx, arg)) if resp.Error ! nil { return } parts, err : arn.Parse(arg) if err ! nil { resp.Error function.ConcatFuncErrors(resp.Error, function.NewFuncError(err.Error())) return } value : map[string]attr.Value{ partition: types.StringValue(parts.Partition), service: types.StringValue(parts.Service), region: types.StringValue(parts.Region), account_id: types.StringValue(parts.AccountID), resource: types.StringValue(parts.Resource), } result, d : types.ObjectValue(arnParseResultAttrTypes, value) if d.HasError() { resp.Error function.ConcatFuncErrors(resp.Error, function.FuncErrorFromDiags(ctx, d)) return } resp.Error function.ConcatFuncErrors(resp.Result.Set(ctx, result)) }第五步将函数注册到 Provider函数实现完成后必须注册到 Provider 才能被使用。由于只有 Terraform Plugin Framework 支持 Provider-defined function注册发生在 Framework Provider 上。原文档中说明的路径为internal/provider/fwprovider/provider.go在当前仓库中对应Functions方法实际位于 internal/provider/framework/provider.gofunc (p *frameworkProvider) Functions(_ context.Context) []func() function.Function { return []func() function.Function{ tffunction.NewARNBuildFunction, tffunction.NewARNParseFunction, tffunction.NewTrimIAMRolePathFunction, tffunction.NewUserAgentFunction, } }新增函数时把New*工厂函数追加到该方法返回的列表末尾即可例如func (p *fwprovider) Functions(_ context.Context) []func() function.Function { return []func() function.Function{ // Append to list of existing functions here tffunction.NewExampleFunction, } }注意函数不遵循资源与数据源使用的自注册流程因此必须手动把工厂函数加入Functions方法这是 function.gtpl 中明确标注的 TIP。同时所有函数名必须唯一由Metadata方法决定。第六步编写单元测试所有函数都应有对应的测试。对于带可变参数variadic arguments或可能返回错误的函数测试应覆盖这些分支。与资源和数据源的 acceptance tests 不同函数测试是纯单元测试函数不接收 Provider 配置因此测试设置极简、执行速度相对较快参见 functiontest.gtpl 中的说明。生成器产出的测试骨架如下// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: MPL-2.0 package function_test import ( fmt testing github.com/hashicorp/go-version github.com/hashicorp/terraform-plugin-testing/helper/resource github.com/hashicorp/terraform-plugin-testing/tfversion github.com/hashicorp/terraform-provider-aws/internal/acctest ) func TestExampleFunction_basic(t *testing.T) { t.Parallel() resource.UnitTest(t, resource.TestCase{ ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.SkipBelow(version.Must(version.NewVersion(1.8.0))), }, Steps: []resource.TestStep{ { Config: testExampleFunctionConfig(foo), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckOutput(test, foo), ), }, }, }) } func testExampleFunctionConfig(arg string) string { return fmt.Sprintf( output test { value provider::aws::example(%[1]q) }, arg) }要点说明Terraform 版本检查Provider-defined function 语法在 Terraform 1.8 才引入因此所有测试都会通过tfversion.SkipBelow(version.Must(version.NewVersion(1.8.0)))在检测到低于 1.8 的 Terraform 时跳过functiontest.gtpl 明确注释了这一原因配置形式函数测试的 Terraform 配置通常只是一个output块在其中以provider::aws::function_name(...)调用被测函数错误用例已知的错误分支应有对应测试验证返回的错误文本生成器骨架默认提供了TestFunctionFunction_invalid使用regexache.MustCompile(argument isnt foo)配合ExpectError断言。在安装了 Terraform 1.8 的环境下可以单独运行某个函数的测试go test -run^TestExampleFunction -v ./internal/function/仓库中 internal/function/arn_parse_test.go 即是按这一模式组织的真实测试包含TestARNParseFunction_known有效输入与TestARNParseFunction_invalid无效输入两类用例。第七步完善注册表文档skaff会在website/docs/functions/function name.html.markdown生成文档骨架其中Example Usage、Signature、Arguments三个部分需要填入真实内容。文档模板结构websitedoc.gtpl包含YAML front mattersubcategory、layout、page_title、description版本提示~ Provider-defined functions are supported in Terraform 1.8 and later.Example Usage一个可复制的 Terraform 配置示例Signature以text代码块给出函数签名例如arn_parse(arn string) objectArguments编号列出的参数说明如1. \arn (String) ARN to parse.。仓库中已发布函数的文档可以作为参照例如 website/docs/functions/arn_parse.html.markdown 中的示例用法# result: # { # partition: aws, # service: iam, # region: , # account_id: 444455556666, # resource: role/example # } output example { value provider::aws::arn_parse(arn:aws:iam::444455556666:role/example) }该文档发布后会随 Provider 版本出现在 Terraform Registry 的函数页面上。第八步提交 PR 与等待优先排序完成实现、测试与文档后按 Raising a Pull Request 的规范提交 Pull Request。一般来说PR 会在创建后几天内完成分诊并按社区反馈情况确定优先级完整流程参见 Prioritization Guide。实战检查清单最后汇总一个新增函数时的自检清单功能是纯计算且可复现不涉及 AWS API 调用否则改用数据源分支名为f-{function name}通过skaff function -n FooBar -d description生成三个文件Metadata中设置了唯一的函数名snake_caseDefinition中参数与返回类型声明完整、描述清晰Run中所有错误路径均通过ConcatFuncErrors聚合resp.Result.Set是Run方法的最后一步且错误被处理工厂函数已追加到internal/provider/framework/provider.go的Functions方法单元测试覆盖正常路径与错误路径并带 Terraform 1.8 版本检查website/docs/functions/下的文档三个核心部分均已更新。【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门