Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tfsdk: GetAttribute panic for nested object in missing/null attribute #207

Closed
bluekeyes opened this issue Oct 12, 2021 · 1 comment · Fixed by #208
Closed

tfsdk: GetAttribute panic for nested object in missing/null attribute #207

bluekeyes opened this issue Oct 12, 2021 · 1 comment · Fixed by #208
Assignees
Labels
bug Something isn't working
Milestone

Comments

@bluekeyes
Copy link
Contributor

Module version

github.com/hashicorp/terraform-plugin-framework v0.4.3-0.20211006203044-2d173e3470f6

Relevant provider source code

I discovered this issue in a resource with this (partial) schema:

return tfsdk.Schema{
    Attributes: map[string]tfsdk.Attribute{
        "patches": {
            Optional: true,
            Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{
                "content": {
                    Type:     types.StringType,
                    Required: true,
                },
                "target": {
                    Optional: true,
                    Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
                        "name": {
                            Type:     types.StringType,
                            Optional: true,
                        },
                        // more optional attributes...
                    }),
                },
            }, tfsdk.ListNestedAttributesOptions{}),
        },
    },
}

I can include additional provider code if needed, but the panic stack is entirely in the framework so hopefully the unit test reproduction included below is sufficient.

Terraform Configuration Files

resource "myprovider_resource" "test" {
  patches = [
    {
      content = file("/path/to/file.patch"),
      target = {
        name = "test"
      }
    }
  ]
}

The resource exists in state and was initially created without the patches property. Adding patches and planning triggers the crash.

Expected Behavior

Terraform executes a plan without crashing, showing patches changing from empty/unset to the content above.

Actual Behavior

The provider panics:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x11e2385]

goroutine 25 [running]:
github.com/hashicorp/terraform-plugin-framework/types.ObjectType.ValueFromTerraform(0xc000174c60, 0x18fe2b0, 0xc00079e140, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-framework/types/object.go:55 +0x65
github.com/hashicorp/terraform-plugin-framework/tfsdk.State.GetAttribute(0x1903f28, 0xc00064e6f0, 0x1737b00, 0xc00017bd10, 0xc000156330, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/state.go:74 +0x624
github.com/hashicorp/terraform-plugin-framework/tfsdk.Attribute.modifyPlan(0x0, 0x0, 0x1904d78, 0xc000156420, 0x0, 0x0, 0x0, 0x0, 0x100, 0x0, ...)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/attribute.go:458 +0x19a
github.com/hashicorp/terraform-plugin-framework/tfsdk.modifyAttributesPlans(0x18fe2b0, 0xc00079e140, 0xc0001563f0, 0xc0004ad368, 0x1903f28, 0xc000121470, 0x1737b00, 0xc000156d50, 0xc000156330, 0x0, ...)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/schema.go:245 +0x528
github.com/hashicorp/terraform-plugin-framework/tfsdk.modifyAttributesPlans(0x18fe2b0, 0xc00079e140, 0xc000156330, 0xc0001450c0, 0x1903f28, 0xc000121470, 0x1737b00, 0xc000156d50, 0xc000156330, 0x0, ...)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/schema.go:275 +0xae8
github.com/hashicorp/terraform-plugin-framework/tfsdk.Schema.modifyAttributePlans(...)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/schema.go:222
github.com/hashicorp/terraform-plugin-framework/tfsdk.(*server).planResourceChange(0xc00046a5a0, 0x18fe2b0, 0xc00079e140, 0xc0005d2460, 0xc000145a90)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/serve.go:810 +0x1848
github.com/hashicorp/terraform-plugin-framework/tfsdk.(*server).PlanResourceChange(0xc00046a5a0, 0x18fe2b0, 0xc00079e100, 0xc0005d2460, 0xc00079e100, 0x17e40a0, 0xc000156000)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/serve.go:635 +0xc8
github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server.(*server).PlanResourceChange(0xc00023ea00, 0x18fe358, 0xc00079e100, 0xc00045e000, 0xc00023ea00, 0xc000156030, 0xc000089ba0)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server/server.go:314 +0xb5
github.com/hashicorp/terraform-plugin-go/tfprotov6/internal/tfplugin6._Provider_PlanResourceChange_Handler(0x17e40a0, 0xc00023ea00, 0x18fe358, 0xc000156030, 0xc000280300, 0x0, 0x18fe358, 0xc000156030, 0xc000168000, 0x42a6)
	/Users/bkeyes/code/my-provider/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/internal/tfplugin6/tfplugin6_grpc.pb.go:363 +0x214
google.golang.org/grpc.(*Server).processUnaryRPC(0xc0003a3180, 0x19052b8, 0xc000603200, 0xc0000c8200, 0xc00046a690, 0x1cf0de8, 0x0, 0x0, 0x0)
	/Users/bkeyes/code/my-provider/vendor/google.golang.org/grpc/server.go:1194 +0x52b
google.golang.org/grpc.(*Server).handleStream(0xc0003a3180, 0x19052b8, 0xc000603200, 0xc0000c8200, 0x0)
	/Users/bkeyes/code/my-provider/vendor/google.golang.org/grpc/server.go:1517 +0xd05
google.golang.org/grpc.(*Server).serveStreams.func1.2(0xc000038290, 0xc0003a3180, 0x19052b8, 0xc000603200, 0xc0000c8200)
	/Users/bkeyes/code/my-provider/vendor/google.golang.org/grpc/server.go:859 +0xab
created by google.golang.org/grpc.(*Server).serveStreams.func1
	/Users/bkeyes/code/my-provider/vendor/google.golang.org/grpc/server.go:857 +0x1fd

Steps to Reproduce

Add this test to StateGetAttributeTest in tfsdk/state_test.go and run the tests:

"WithAttributeName-ListNestedAttributes-null-WithElementKeyInt-WithAttributeName-object": {
    state: State{
        Raw: tftypes.NewValue(tftypes.Object{
            AttributeTypes: map[string]tftypes.Type{
                "test": tftypes.List{
                    ElementType: tftypes.Object{
                        AttributeTypes: map[string]tftypes.Type{
                            "sub_test": tftypes.Object{
                                AttributeTypes: map[string]tftypes.Type{
                                    "value": tftypes.String,
                                },
                            },
                        },
                    },
                },
                "other": tftypes.Bool,
            },
        }, map[string]tftypes.Value{
            "test": tftypes.NewValue(tftypes.List{
                ElementType: tftypes.Object{
                    AttributeTypes: map[string]tftypes.Type{
                        "sub_test": tftypes.Object{
                            AttributeTypes: map[string]tftypes.Type{
                                "value": tftypes.String,
                            },
                        },
                    },
                },
            }, nil),
            "other": tftypes.NewValue(tftypes.Bool, nil),
        }),
        Schema: Schema{
            Attributes: map[string]Attribute{
                "test": {
                    Attributes: ListNestedAttributes(map[string]Attribute{
                        "sub_test": {
                            Attributes: SingleNestedAttributes(map[string]Attribute{
                                "value": {
                                    Type:     types.StringType,
                                    Optional: true,
                                },
                            }),
                            Optional: true,
                        },
                    }, ListNestedAttributesOptions{}),
                    Optional: true,
                },
                "other": {
                    Type:     types.BoolType,
                    Optional: true,
                },
            },
        },
    },
    path: tftypes.NewAttributePath().WithAttributeName("test").WithElementKeyInt(0).WithAttributeName("sub_test"),
    expected: types.Object{
        Null:      true,
        AttrTypes: map[string]attr.Type{"value": types.StringType},
    },
},

References

Other Thoughts

In types.Object#ValueFromTerraform, I can fix the failing test by either:

  1. Adding a check that in.Type() is not nil before calling Equal
  2. Moving the IsKnown and IsNull checks before the type check

I can submit a PR with the test and a fix, but I'm not sure which option is best, or if there are any other similar cases that need fixes.

@bluekeyes bluekeyes added the bug Something isn't working label Oct 12, 2021
@bflad bflad self-assigned this Nov 2, 2021
@bflad bflad added this to the v0.5.0 milestone Nov 11, 2021
@github-actions
Copy link

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 12, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants