改变了Terraform插件框架的属性



我正在用新的terraform插件框架创建一个自定义的地形提供程序。

我想保存像这样的对象到terraform状态:

"attributes": {
"name_servers": {
"ns1": {
"host": "host1",
"id": 100,
"ip": "",
"is_used": true
},
"ns2": {
"host": "host2",
"id": 101,
"ip": "",
"is_used": true,
}
},
"domain": "example.com"
},

它与以下地形资源一起工作:

...
type FooResourceModel struct {
Domain types.String                `tfsdk:"domain"`
NameServers map[string]NameServersModel `tfsdk:"name_servers"`
}
type NameServersModel struct {
ID       types.Int64  `tfsdk:"id"`
Host     types.String `tfsdk:"host"`
IP       types.String `tfsdk:"ip"`
IsUsed   types.Bool   `tfsdk:"is_used"`
}

func (r *FooNSResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"domain": schema.StringAttribute{
Required:            true,
},
"name_servers": schema.MapNestedAttribute{
Required: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.Int64Attribute{
Computed: true,
},
"host": schema.StringAttribute{
Optional: true,
Computed: true,
},
"ip": schema.StringAttribute{
Optional: true,
Computed: true,
},
"is_used": schema.BoolAttribute{
Computed: true,
},
},
},
},
},
}
}
func (r *FooNSResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data *FooNSResourceModel
diags := req.State.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
serviceName := data.ServiceName.ValueString()
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
nameServers, err := getNs(r, serviceName)
data.NameServers = nameServers
diags = resp.State.Set(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func getNs(r *FooNSResource, domain string) (map[string]NameServersModel, error) {
nameServers := make(map[string]NameServersModel)
var ids []uint64
err := r.client.Get(domain, &ids)
if err != nil {
return nil, err
}
for key, id := range ids {
nsResponse := NameServerResponse{}
err := r.client.Get(id, &nsResponse)
if err != nil {
return nil, err
}
nameServers["ns"+strconv.Itoa(key+1)] = NameServersModel{
ID:       types.Int64Value(int64(nsResponse.Id)),
Host:     types.StringValue(nsResponse.GetHost()),
IP:       types.StringValue(nsResponse.GetIP()),
IsUsed:   types.BoolValue(nsResponse.IsUsed),
}
}
return nameServers, nil
}
func (r *FooNSResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
serviceName := req.ID
nameServers, err := getNs(r, serviceName)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("domain"), serviceName)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name_servers"), nameServers)...)
}
...

所以当我从API导入数据时,它将其保存为地形状态。

在我的Terraform文件中:

...
resource "my_custom_provider" "my-server" {
service_name = "example.com"

name_servers = {
"ns1" = {
host = "host1"
},
"ns2" = {
host = "host2"
},
}
}
...

当我运行terraform plan时,它显示一切正常:

No changes. Your infrastructure matches the configuration.

但是当我添加ip值时:

...
resource "my_custom_provider" "my-server" {
service_name = "example.com"

name_servers = {
"ns1" = {
host = "host1"
},
"ns2" = {
host = "host2"
ip = "127.0.0.1"
},
}
}
...

那么Terraform想要更新一切:

Terraform will perform the following actions:
# my_custom_provider.my-server will be updated in-place
~ resource "my_custom_provider" "my-server" {
~ name_servers = {
~ "ns1" = {
~ id        = 100 -> (known after apply)
~ ip        = "" -> (known after apply)
~ is_used   = true -> (known after apply)
# (1 unchanged attribute hidden)
},
~ "ns2" = {
~ id        = 101 -> (known after apply)
~ ip        = "" -> "127.0.0.1"
~ is_used   = true -> (known after apply)
# (1 unchanged attribute hidden)
},
}
# (1 unchanged attribute hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.

name_servers中未改变的属性是我的主机。

问题是:为什么Terraform认为一切都会改变?我如何防止它,所以只有ns1或ns2值host/ip将被更新?

我找到了一个解决方案- PlanModifier: https://developer.hashicorp.com/terraform/plugin/framework/resources/plan-modification#common-use-case-attribute-plan-modifiers

使用PlanModifierUseStateForUnknown()功能:

UseStateForUnknown():复制先前的状态值,如果不是null。这对于减少(应用后知道的)计算属性的计划输出很有用,这些属性已知不会随时间变化。

Schema现在看起来是这样的:

NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.Int64Attribute{
Computed: true,
PlanModifiers: []planmodifier.Int64{
int64planmodifier.UseStateForUnknown(),
},
},
"host": schema.StringAttribute{
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"ip": schema.StringAttribute{
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"is_used": schema.BoolAttribute{
Computed: true,
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.UseStateForUnknown(),
},
},
},
},

对于每个属性,我都添加了计划修饰符,现在在地形计划中输出看起来应该是这样的。

我不知道这是否是一个最好的解决方案,但它是有效的:)在Terraform 1.4.2上测试。

最新更新