上下文:我正在实现(我的第一个(Terraform插件/提供程序作为现有公共API的包装器。
API 中的创建操作之一指定一个整数字段,该字段采用正值或-1
作为默认值。如果在创建 API 调用中指定-1
,则该值将在服务器端(例如field = 1000
(替换为某个默认值,并从现在开始存储为1000
。
如果我将其呈现给我的 Terraform 插件 (terraform apply
(:
resource "something" "mysomething" {
name = "someName"
field = -1
}
调用不是幂等的。Terraform继续看到漂移,随后提供:
# something.mysomething will be updated in-place
~ resource "something" "mysomething" {
id = "165-1567498530352"
name = "someName"
~ field = 1000 -> -1
}
Plan: 0 to add, 1 to change, 0 to destroy.
如何处理这样的API?
Terraform SDK包含一个特殊的模式标志Computed
,这意味着"如果配置中没有给出值,那么将在应用时选择默认值"。
这似乎与您在此处的用例相匹配。如果取消设置Default
并改为设置Computed: true
(保留Optional: true
以指示用户可以选择设置它(,则可以激活该行为。
如果您能够在计划步骤中预测最终的"计算"值,则在创建或更新任何内容之前,您应该为资源实现CustomizeDiff
并使用d.Set
来提供值,然后 Terraform 可以将其考虑在内以生成更完整的计划。
如果没有,则可以在规划期间将其保留为未设置(在 Terraform 术语中,其值将为"未知"(,然后在Create
和Update
函数中调用d.Set
,值在计划中显示为(known after apply)
。
使用此机制时,必须自洽:如果在使用CustomizeDiff
规划期间提供已知值,则该值必须与Create
或Update
期间选择的最终值完全匹配。如果您不一致,则当 Terraform Core 验证最终更改是否与计划一致时,其他表达式中对此属性的任何引用都会导致应用期间出错。
这种方法目前有一个警告:由于目前 Terraform SDK 的 API 设计限制,提供程序代码无法判断以前在配置中设置的值现在何时不再设置。或者,换句话说,SDK 无法判断已存储的值是通过显式配置选择的,还是由提供程序在应用期间填充为默认值。
因此,如果用户取消设置配置中的最后一个值,则该值将是"粘性的",并且提供程序将无法自动将该值调整回服务器提供的默认值。
在实践中,这个警告通常不是什么大问题,但值得注意的是,以防它在你的具体情况中确实很重要。SDK 的后续版本可能会提供一种机制来询问是否在配置中设置了特定值,与存储在状态中的内容分开。
您可以在架构属性上使用DiffSuppressFunc
标志有条件地抑制差异,以便 Terraform 不会选择对差异执行任何操作。
这样的东西应该适合你:
package something
import (
"github.com/hashicorp/terraform/helper/schema"
)
func somethingSomething() *schema.Resource {
return &schema.Resource{
// ...
Schema: map[string]*schema.Schema{
// ...
"field": {
Type: schema.TypeInt,
Optional: true,
Default: -1,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if new == "-1" {
return true
}
return false
},
},
},
}
}
Martin 的回答可能通过使用Computed
标志并使属性Optional
提供了更好的选择。为了很好地工作,您需要防止人们将-1
指定为此的值,您可以使用核心 SDK 中预定义验证列表中的IntAtLeast
验证器来使用ValidateFunc
:
package something
import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
)
func somethingSomething() *schema.Resource {
return &schema.Resource{
// ...
Schema: map[string]*schema.Schema{
// ...
"field": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validation.IntAtLeast(1),
},
},
}
}