Skip to content

Commit

Permalink
tfsdk: Support for List and Set Blocks
Browse files Browse the repository at this point in the history
Reference: #85

This implementation is analogous to the existing `Attributes` field on `Attribute`. While the framework handles the major differences at the protocol layer during conversion, it also must enforce the constraints of the underlying type system.

Some notable features include:

* Defining schemas is very similar to `Attributes`.
* Accessing and writing `Config`, `Plan`, and `State` data is no different than `Attributes` with the same nesting mode.
* Blocks are structural by Terraform's and cty's definition, meaning there is no concept of `Computed`, `Optional`, `Required`, or `Sensitive`. Checks are in place to enforce these constraints.

The primary purpose for supporting blocks is to allow previously existing schemas defined by the older Terraform Plugin SDK to not require practitioner breaking changes upon migrating to the framework (except the protocol version and therefore the minimum Terraform CLI version). This also allows this framework to be muxed with the older framework since the provider schema must match. It is expected that over time any schema definitions including `Blocks` will migrate to `Attributes`.

Provider developers should always opt for `Attributes` in new schema definitions.
  • Loading branch information
bflad committed Sep 29, 2021
1 parent 4b382e2 commit 09b2909
Show file tree
Hide file tree
Showing 12 changed files with 3,749 additions and 154 deletions.
589 changes: 453 additions & 136 deletions tfsdk/attribute.go

Large diffs are not rendered by default.

912 changes: 905 additions & 7 deletions tfsdk/attribute_test.go

Large diffs are not rendered by default.

200 changes: 200 additions & 0 deletions tfsdk/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,100 @@ func TestConfigGetAttribute(t *testing.T) {
path: tftypes.NewAttributePath().WithAttributeName("test").WithElementKeyInt(0).WithAttributeName("sub_test"),
expected: types.String{Value: "value"},
},
"WithAttributeName-ListNestedBlocks-null-WithElementKeyInt-WithAttributeName": {
config: Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.List{
ElementType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": 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.String,
},
},
}, nil),
"other": tftypes.NewValue(tftypes.Bool, nil),
}),
Schema: Schema{
Attributes: map[string]Attribute{
"test": {
Blocks: ListNestedBlocks(map[string]Attribute{
"sub_test": {
Type: types.StringType,
Required: true,
},
}, ListNestedBlocksOptions{}),
},
"other": {
Type: types.BoolType,
Optional: true,
},
},
},
},
path: tftypes.NewAttributePath().WithAttributeName("test").WithElementKeyInt(0).WithAttributeName("sub_test"),
expected: types.String{Null: true},
},
"WithAttributeName-ListNestedBlocks-WithElementKeyInt-WithAttributeName": {
config: Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.List{
ElementType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": 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.String,
},
},
}, []tftypes.Value{
tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": tftypes.String,
},
}, map[string]tftypes.Value{
"sub_test": tftypes.NewValue(tftypes.String, "value"),
}),
}),
"other": tftypes.NewValue(tftypes.Bool, nil),
}),
Schema: Schema{
Attributes: map[string]Attribute{
"test": {
Blocks: ListNestedBlocks(map[string]Attribute{
"sub_test": {
Type: types.StringType,
Required: true,
},
}, ListNestedBlocksOptions{}),
},
"other": {
Type: types.BoolType,
Optional: true,
},
},
},
},
path: tftypes.NewAttributePath().WithAttributeName("test").WithElementKeyInt(0).WithAttributeName("sub_test"),
expected: types.String{Value: "value"},
},
"WithAttributeName-Map-null-WithElementKeyString": {
config: Config{
Raw: tftypes.NewValue(tftypes.Object{
Expand Down Expand Up @@ -799,6 +893,112 @@ func TestConfigGetAttribute(t *testing.T) {
})).WithAttributeName("sub_test"),
expected: types.String{Value: "value"},
},
"WithAttributeName-SetNestedBlocks-null-WithElementKeyValue-WithAttributeName": {
config: Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.Set{
ElementType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": tftypes.String,
},
},
},
"other": tftypes.Bool,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.Set{
ElementType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": tftypes.String,
},
},
}, nil),
"other": tftypes.NewValue(tftypes.Bool, nil),
}),
Schema: Schema{
Attributes: map[string]Attribute{
"test": {
Blocks: SetNestedBlocks(map[string]Attribute{
"sub_test": {
Type: types.StringType,
Required: true,
},
}, SetNestedBlocksOptions{}),
},
"other": {
Type: types.BoolType,
Optional: true,
},
},
},
},
path: tftypes.NewAttributePath().WithAttributeName("test").WithElementKeyValue(tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": tftypes.String,
},
}, map[string]tftypes.Value{
"sub_test": tftypes.NewValue(tftypes.String, "value"),
})).WithAttributeName("sub_test"),
expected: types.String{Null: true},
},
"WithAttributeName-SetNestedBlocks-WithElementKeyValue-WithAttributeName": {
config: Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.Set{
ElementType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": tftypes.String,
},
},
},
"other": tftypes.Bool,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.Set{
ElementType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": tftypes.String,
},
},
}, []tftypes.Value{
tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": tftypes.String,
},
}, map[string]tftypes.Value{
"sub_test": tftypes.NewValue(tftypes.String, "value"),
}),
}),
"other": tftypes.NewValue(tftypes.Bool, nil),
}),
Schema: Schema{
Attributes: map[string]Attribute{
"test": {
Blocks: SetNestedBlocks(map[string]Attribute{
"sub_test": {
Type: types.StringType,
Required: true,
},
}, SetNestedBlocksOptions{}),
},
"other": {
Type: types.BoolType,
Optional: true,
},
},
},
},
path: tftypes.NewAttributePath().WithAttributeName("test").WithElementKeyValue(tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"sub_test": tftypes.String,
},
}, map[string]tftypes.Value{
"sub_test": tftypes.NewValue(tftypes.String, "value"),
})).WithAttributeName("sub_test"),
expected: types.String{Value: "value"},
},
"WithAttributeName-SingleNestedAttributes-null-WithAttributeName": {
config: Config{
Raw: tftypes.NewValue(tftypes.Object{
Expand Down
11 changes: 8 additions & 3 deletions tfsdk/nested_attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import (
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// NestingMode is an enum type of the ways nested attributes can be nested.
// They can be a list, a set, or a map (with string keys), or they can be
// nested directly, like an object.
// NestingMode is an enum type of the ways nested attributes can be nested in
// an attribute or a block. They can be a list, a set, a map (with string
// keys), or they can be nested directly, like an object.
//
// While the protocol and theoretically Terraform itself support map, single,
// and group nesting modes, this framework intentionally only supports list
// and set for blocks as those other modes were not typically implemented or
// tested since the older Terraform Plugin SDK did not support them.
type NestingMode uint8

const (
Expand Down
Loading

0 comments on commit 09b2909

Please sign in to comment.