Open maxb opened 1 year ago
Related: https://github.com/hashicorp/terraform-plugin-framework/issues/709 (they will likely get fixed as best as possible at the same time)
For other maintainer's benefit, here's the relevant stack trace from framework v1.3.1:
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: panic: runtime error: invalid memory address or nil pointer dereference
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: [signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x642887]
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug:
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: goroutine 14 [running]:
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: github.com/hashicorp/terraform-plugin-framework/internal/fwserver.(*Server).PlanResourceChange(0xc0001d54a0, {0xc54480, 0xc0000f2c60}, 0xc000144780, 0xc0001ff4f0)
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: /home/maxb/.cache/go-modules/github.com/hashicorp/terraform-plugin-framework@v1.3.1/internal/fwserver/server_planresourcechange.go:210 +0x20c7
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: github.com/hashicorp/terraform-plugin-framework/internal/proto6server.(*Server).PlanResourceChange(0xc0001d54a0, {0xc54480?, 0xc0000f2b10?}, 0xc000144640)
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: /home/maxb/.cache/go-modules/github.com/hashicorp/terraform-plugin-framework@v1.3.1/internal/proto6server/server_planresourcechange.go:55 +0x41a
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server.(*server).PlanResourceChange(0xc000252500, {0xc54480?, 0xc0000f2150?}, 0xc0002ce070)
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: /home/maxb/.cache/go-modules/github.com/hashicorp/terraform-plugin-go@v0.16.0/tfprotov6/tf6server/server.go:784 +0x574
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: github.com/hashicorp/terraform-plugin-go/tfprotov6/internal/tfplugin6._Provider_PlanResourceChange_Handler({0xb2eee0?, 0xc000252500}, {0xc54480, 0xc0000f2150}, 0xc0002ce000, 0x0)
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: /home/maxb/.cache/go-modules/github.com/hashicorp/terraform-plugin-go@v0.16.0/tfprotov6/internal/tfplugin6/tfplugin6_grpc.pb.go:404 +0x170
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: google.golang.org/grpc.(*Server).processUnaryRPC(0xc0002481e0, {0xc58458, 0xc00040a1a0}, 0xc0000f4000, 0xc0003a8000, 0x108a008, 0x0)
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: /home/maxb/.cache/go-modules/google.golang.org/grpc@v1.56.0/server.go:1337 +0xdf3
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: google.golang.org/grpc.(*Server).handleStream(0xc0002481e0, {0xc58458, 0xc00040a1a0}, 0xc0000f4000, 0x0)
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: /home/maxb/.cache/go-modules/google.golang.org/grpc@v1.56.0/server.go:1714 +0xa36
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: google.golang.org/grpc.(*Server).serveStreams.func1.1()
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: /home/maxb/.cache/go-modules/google.golang.org/grpc@v1.56.0/server.go:959 +0x98
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: created by google.golang.org/grpc.(*Server).serveStreams.func1
2023-06-18T17:37:34.030+0100 [DEBUG] provider.terraform-provider-bug: /home/maxb/.cache/go-modules/google.golang.org/grpc@v1.56.0/server.go:957 +0x18c
And the associated code:
While plannedState
could/should have a nil
check there, ignoring the errors likely would indicate something else amiss.
As part of further triaging this, returning the resp.PlannedState.GetAttribute
diagnostic yields:
| Error: Duplicate Set Element
|
| with framework_bug.test,
| on terraform_plugin_test.tf line 2, in resource "framework_bug" "test":
| 2: \t\t\t\t\tset = [
| 3: \t\t\t\t\t\t{ fruit = "apple" },
| 4: \t\t\t\t\t\t{ fruit = "banana" },
| 5: \t\t\t\t\t\t{ fruit = "kumquat" },
| 6: \t\t\t\t\t]
|
| This attribute contains duplicate values of:
| tftypes.Object["fruit":tftypes.String,
| "other":tftypes.String]<"fruit":tftypes.String<"orange">,
| "other":tftypes.String<"other">>
Which is due to the (basetypes.SetValue).Validate()
implementation. The precursor logging being:
2023-07-11T14:02:11.824-0400 [TRACE] sdk.framework: setting attribute set[Value({"fruit":"apple","other":"other"})].other to default value: "other": tf_resource_type=framework_bug tf_req_id=0cc087b6-31dc-9fcb-aca4-ff58d9a5aa83 tf_provider_addr=registry.terraform.io/hashicorp/framework tf_rpc=PlanResourceChange
2023-07-11T14:02:11.824-0400 [TRACE] sdk.framework: setting attribute set[Value({"fruit":"apple","other":"other"})].fruit to default value: "orange": tf_resource_type=framework_bug tf_req_id=0cc087b6-31dc-9fcb-aca4-ff58d9a5aa83 tf_provider_addr=registry.terraform.io/hashicorp/framework tf_rpc=PlanResourceChange
2023-07-11T14:02:11.824-0400 [TRACE] sdk.framework: attribute is a non-schema attribute, not setting default: tf_req_id=0cc087b6-31dc-9fcb-aca4-ff58d9a5aa83 tf_provider_addr=registry.terraform.io/hashicorp/framework tf_rpc=PlanResourceChange tf_resource_type=framework_bug
2023-07-11T14:02:11.824-0400 [TRACE] sdk.framework: setting attribute set[Value({"fruit":"banana","other":"other"})].fruit to default value: "orange": tf_resource_type=framework_bug tf_req_id=0cc087b6-31dc-9fcb-aca4-ff58d9a5aa83 tf_provider_addr=registry.terraform.io/hashicorp/framework tf_rpc=PlanResourceChange
2023-07-11T14:02:11.824-0400 [TRACE] sdk.framework: setting attribute set[Value({"fruit":"banana","other":"other"})].other to default value: "other": tf_resource_type=framework_bug tf_req_id=0cc087b6-31dc-9fcb-aca4-ff58d9a5aa83 tf_provider_addr=registry.terraform.io/hashicorp/framework tf_rpc=PlanResourceChange
2023-07-11T14:02:11.824-0400 [TRACE] sdk.framework: attribute is a non-schema attribute, not setting default: tf_req_id=0cc087b6-31dc-9fcb-aca4-ff58d9a5aa83 tf_provider_addr=registry.terraform.io/hashicorp/framework tf_rpc=PlanResourceChange tf_resource_type=framework_bug
2023-07-11T14:02:11.824-0400 [TRACE] sdk.framework: setting attribute set[Value({"fruit":"kumquat","other":"other"})].fruit to default value: "orange": tf_rpc=PlanResourceChange tf_resource_type=framework_bug tf_req_id=0cc087b6-31dc-9fcb-aca4-ff58d9a5aa83 tf_provider_addr=registry.terraform.io/hashicorp/framework
2023-07-11T14:02:11.824-0400 [TRACE] sdk.framework: setting attribute set[Value({"fruit":"kumquat","other":"other"})].other to default value: "other": tf_provider_addr=registry.terraform.io/hashicorp/framework tf_rpc=PlanResourceChange tf_resource_type=framework_bug tf_req_id=0cc087b6-31dc-9fcb-aca4-ff58d9a5aa83
2023-07-11T14:02:11.824-0400 [TRACE] sdk.framework: attribute is a non-schema attribute, not setting default: tf_req_id=0cc087b6-31dc-9fcb-aca4-ff58d9a5aa83 tf_provider_addr=registry.terraform.io/hashicorp/framework tf_rpc=PlanResourceChange tf_resource_type=framework_bug
As you mention @maxb, there's a consternation of set values where the value itself represents its identity. While this property is due to the design of Terraform's type system, the default implementation is clearly not doing the right thing here. One potential option here would be walking set elements as slice indexes during this transformation, which matches how other internal framework logic handles other walks/transformations to avoid the set identity issue. The longer term option is disregarding Terraform's ProposedNewState
data completely and re-implementing core's logic for list/set type alignment to generate our own ProposedNewState
data, which was the unspoken plan for #709.
Another instance of this bug was reported in our discuss forums: https://discuss.hashicorp.com/t/setnestedattribute-executing-terraform-apply-gets-an-error-error-provider-produced-inconsistent-result-after-apply/67886/
Module version
Relevant provider source code
This is the entire source code of a single .go file provider that demonstrates the issues:
Terraform Configuration Files
Debug Output
https://gist.github.com/maxb/f0b606530531a1f7e89c1ac122f141da
Steps to Reproduce
go.mod
including the latest version of terraform-plugin-framework (no other dependencies needed), build the provider.dev_overrides
so you can test the provider.terraform apply -auto-approve
(no initial state is needed)terraform apply -auto-approve
again ... the provider panics/crashesSecondary related bug:
terraform.tfstate
from the above reproductionterraform apply -auto-approve
... observe that provider repeatedly changes the value back and forth on each run, oscillating between the value actually written in the configuration, and the value set as a default in the code.Partial diagnosis
The terraform-plugin-framework appears to use a bafflingly complex algorithm to apply defaults, involving correlating the state and the configuration.
This is a victim of its own complexity when dealing with sets of objects, as the identity of an object within a set incorporates its own value ... a value that may itself have defaults. This means the identity of a set member in the config may omit unspecified attributes, whilst the identity of the same set member in the state will include unspecified attributes, now set to their default values.