Skip to content

Commit

Permalink
website: Updates for tfsdk Attribute, Block, and Schema deprecations (#…
Browse files Browse the repository at this point in the history
…564)

Reference: #132
Reference: #326
Reference: #437
Reference: #491
Reference: #546
Reference: #553
Reference: #558
Reference: #562
Reference: #563
  • Loading branch information
bflad committed Nov 30, 2022
1 parent 4db7ec6 commit d51781c
Show file tree
Hide file tree
Showing 32 changed files with 613 additions and 796 deletions.
13 changes: 6 additions & 7 deletions website/docs/plugin/framework/acctests.mdx
Expand Up @@ -61,19 +61,18 @@ testing_new.go:53: no "id" found in attributes

To avoid this, add a root level `id` attribute to resource and data source schemas. Ensure the attribute value is appropriately [written to state](/plugin/framework/writing-state). Conventionally, `id` is a computed attribute that contains the identifier for the resource.

For example, in the `GetSchema` method implementation of a [`datasource.DataSource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/datasource#DataSource) or [`resource.Resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource):
For example, in the `Schema` method implementation of a [`datasource.DataSource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/datasource#DataSource) or [`resource.Resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource):

```go
func (r *ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
// ... potentially other schema configuration ...
Attributes: map[string]tfsdk.Attribute{
Attributes: map[string]schema.Attribute{
// ... potentially other schema attributes ...
"id": {
Type: types.StringType,
"id": schema.StringAttribute{
Computed: true,
},
},
}, nil
}
}
```
Expand Up @@ -39,42 +39,24 @@ func resourceExample() *schema.Resource {
## Framework
In the Framework, you define attributes by setting the `Attributes` field on your provider, resource, or data type's
schema, as returned by the `GetSchema` function. The `tfsdk.Schema` type returned by `GetSchema` includes an
`Attributes` field that maps each attribute name (string) to the attribute's `tfsdk.Attribute` struct.
schema, as returned by the `Schema` method. The `schema.Schema` type returned by `SchemaResponse` includes an
`Attributes` field that maps each attribute name (string) to the attribute's definition.
The following code shows how to define an attribute for a resource with the Framework.
```go
func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"example": {
/* ... */
func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"example": /* ... */
```
The Framework defines the `tfsdk.Attribute` struct as follows.
```go
type Attribute struct {
Type attr.Type
Attributes NestedAttributes
Description string
MarkdownDescription string
Required bool
Optional bool
Computed bool
Sensitive bool
DeprecationMessage string
Validators []AttributeValidator
PlanModifiers AttributePlanModifiers
}
```
## Migration Notes
Remember the following differences between SDKv2 and the Framework when completing the migration.
- In SDKv2, attributes are defined by a map from attribute names to `schema.Schema` structs in the `Schema` field of
your resource's schema. In the Framework, attributes are defined by a map from attribute names to `tfsdk.Attribute`
structs in your resource's schema, which is returned by the `GetSchema` function.
your resource's schema. In the Framework, attributes are defined by a map from attribute names to `schema.Attribute`
implementations in your resource's schema, which is returned by the resource `Schema` method.
- There are several differences between the implementation of attributes in SDKv2 and the Framework. Refer to the other
pages in the Attributes & Blocks section of this migration guide for more details.
Expand Down Expand Up @@ -115,13 +97,12 @@ The following shows the same section of provider code after the migration.
This code implements the `url` attribute for the `http` data source with the Framework.
```go
func (d *httpDataSource) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
func (d *httpDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
return schema.Schema{
/* ... */
Attributes: map[string]tfsdk.Attribute{
"url": {
Attributes: map[string]schema.Attribute{
"url": schema.StringAttribute{
Description: "The URL for the request. Supported schemes are `http` and `https`.",
Type: types.StringType,
Required: true,
},
/* ... */
Expand Down
Expand Up @@ -36,41 +36,39 @@ map[string]*schema.Schema{
## Framework
In the Framework, when working with protocol version 5, computed blocks are implemented using an attribute with a `Type`
of `types.ListType` which has an `ElemType` of `types.ObjectType`.
In the Framework, when working with protocol version 5, computed blocks are implemented using a `ListAttribute` which has an `ElementType` of `types.ObjectType`.
```go
map[string]tfsdk.Attribute{
"example": {
map[string]schema.Attribute{
"example": schema.ListAttribute{
Computed: true,
Type: types.ListType{
ElemType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"nested_example": types.StringType,
/* ... */
ElementType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"nested_example": types.StringType,
/* ... */

```
In the Framework, when working with protocol version 6, we recommend that you define computed blocks using nested
attributes. This example shows usage of `ListNestedAttributes` as this provides configuration references with list index
syntax as is the case when using `schema.TypeList` in SDKv2. `SingleNestedAttributes` is a good choice for single
attributes. This example shows usage of `ListNestedAttribute` as this provides configuration references with list index
syntax as is the case when using `schema.TypeList` in SDKv2. `SingleNestedAttribute` is a good choice for single
underlying objects which results in a breaking change but also allows dropping `[0]` in configuration references.
```go
map[string]tfsdk.Attribute{
"example": {
Computed: true,
Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{
"nested_example": {
Computed: true,
Type: types.StringType,
/* ... */
map[string]schema.Attribute{
"example": schema.ListNestedAttribute{
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"nested_example": schema.StringAttribute{
Computed: true,
/* ... */

```
## Migration Notes
- When using protocol version 5 specify `ElemType` as `types.ObjectType` when migrating blocks that are computed from SDKv2 to Framework.
- When using protocol version 5 specify `ElementType` as `types.ObjectType` when migrating blocks that are computed from SDKv2 to Framework.
- When using protocol version 6, use [nested attributes](/plugin/framework/schemas#attributes-1) when migrating blocks that are computed from SDKv2 to Framework.
## Example
Expand Down Expand Up @@ -111,12 +109,11 @@ This code defines the `certificates` block as an attribute on the `certificate`
`types.ObjectType` is being used to define the nested block.
```go
map[string]tfsdk.Attribute{
"certificates": {
Type: types.ListType{
ElemType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"signature_algorithm": types.StringType,
map[string]schema.Attribute{
"certificates": schema.ListAttribute{
ElementType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"signature_algorithm": types.StringType,
},
},
Computed: true,
Expand Down
Expand Up @@ -59,22 +59,20 @@ func resourceExample() *schema.Resource {
## Framework
In the Framework, you implement nested blocks with the `Blocks` field of your provider, resource, or data source's
`tfsdk.Schema`, as returned by the `GetSchema` function. The `Blocks` field maps the name of each block to a
`tfsdk.Block` struct which defines the block's behavior.
schema, as returned by the `Schema` method. The `Blocks` field maps the name of each block to a
`schema.Block` definition.
```go
func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
return schema.Schema{
/* ... */
Blocks: map[string]tfsdk.Block{
"example": {
NestingMode: tfsdk.BlockNestingModeList,
MaxItems: int,
Attributes: map[string]tfsdk.Attribute{
"nested_example": {
Type: types.StringType,
Optional: bool
/* ... */
Blocks: map[string]schema.Block{
"example": schema.ListNestedBlock{
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"nested_example": schema.StringAttribute{
Optional: bool
/* ... */
```
Expand Down Expand Up @@ -122,24 +120,22 @@ The following example from the `resource_cert_request.go` file shows how the nes
`cert_request` resource is defined with the Framework after the migration.
```go
tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"private_key_pem": {
Type: types.StringType,
/* ... */

Blocks: map[string]tfsdk.Block{
"subject": {
NestingMode: tfsdk.BlockNestingModeList,
MinItems: 0,
MaxItems: 1,
Attributes: map[string]tfsdk.Attribute{
"organization": {
Type: types.StringType,
/* ... */
},
"common_name": {
Type: types.StringType,
/* ... */
},
schema.Schema{
Attributes: map[string]schema.Attribute{
"private_key_pem": schema.StringAttribute{
/* ... */

Blocks: map[string]schema.Block{
"subject": schema.ListNestedBlock{
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"organization": schema.StringAttribute{
/* ... */
},
"common_name": schema.StringAttribute{
/* ... */
},
Validators: validator.List{
listvalidator.SizeAtMost(1),
},
```
Expand Up @@ -39,16 +39,15 @@ func resourceExample() *schema.Resource {
## Framework
In the Framework, you set default values with the `PlanModifiers` field on your attribute's `tfsdk.Attribute`
struct.
In the Framework, you set default values with the `PlanModifiers` field on your attribute's definition.
```go
func (r *resourceExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
func (r *resourceExample) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
/* ... */
Attributes: map[string]tfsdk.Attribute{
"attribute_example": {
PlanModifiers: []tfsdk.AttributePlanModifier{
Attributes: map[string]schema.Attribute{
"attribute_example": schema.BoolAttribute{
PlanModifiers: []planmodifier.Bool{
defaultValue(types.BoolValue(true)),
/* ... */
```
Expand Down Expand Up @@ -93,12 +92,12 @@ The following shows the same section of code after the migration.
This code implements the `PlanModifiers` field for the `rsa_bits` attribute with the Framework.
```go
func (r *privateKeyResource) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"rsa_bits": {
PlanModifiers: []tfsdk.AttributePlanModifier{
attribute_plan_modifier.DefaultValue(types.Int64Value(2048)),
func (r *privateKeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"rsa_bits": schema.Int64Attribute{
PlanModifiers: []planmodifier.Int64{
attribute_plan_modifier.Int64DefaultValue(types.Int64Value(2048)),
/* ... */
},
/* ... */
Expand All @@ -113,36 +112,36 @@ The following example from the `attribute_plan_modifiers.go` file implements the
that the `rsa_bits` attribute uses.
```go
func DefaultValue(v attr.Value) tfsdk.AttributePlanModifier {
return &defaultValueAttributePlanModifier{v}
func Int64DefaultValue(v types.Int64) planmodifier.Int64 {
return &int64DefaultValuePlanModifier{v}
}

type defaultValueAttributePlanModifier struct {
DefaultValue attr.Value
type int64DefaultValuePlanModifier struct {
DefaultValue types.Int64
}

var _ tfsdk.AttributePlanModifier = (*defaultValueAttributePlanModifier)(nil)
var _ planmodifier.Int64 = (*int64DefaultValuePlanModifier)(nil)

func (apm *defaultValueAttributePlanModifier) Description(ctx context.Context) string {
func (apm *int64DefaultValuePlanModifier) Description(ctx context.Context) string {
/* ... */
}

func (apm *defaultValueAttributePlanModifier) MarkdownDescription(ctx context.Context) string {
func (apm *int64DefaultValuePlanModifier) MarkdownDescription(ctx context.Context) string {
/* ... */
}

func (apm *defaultValueAttributePlanModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, res *tfsdk.ModifyAttributePlanResponse) {
func (apm *int64DefaultValuePlanModifier) PlanModifyInt64(ctx context.Context, req planmodifier.Int64Request, res *planmodifier.Int64Response) {
// If the attribute configuration is not null, we are done here
if !req.AttributeConfig.IsNull() {
if !req.ConfigValue.IsNull() {
return
}

// If the attribute plan is "known" and "not null", then a previous plan modifier in the sequence
// has already been applied, and we don't want to interfere.
if !req.AttributePlan.IsUnknown() && !req.AttributePlan.IsNull() {
if !req.PlanValue.IsUnknown() && !req.PlanValue.IsNull() {
return
}

res.AttributePlan = apm.DefaultValue
res.PlanValue = apm.DefaultValue
}
```
Expand Up @@ -33,14 +33,14 @@ func resourceExample() *schema.Resource {
## Framework
In the Framework, you set the same fields on the `tfsdk.Attribute` struct, with the same behavior.
In the Framework, you set the same fields on the `schema.Attribute` implementation, with the same behavior.
```go
func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
/* ... */
Attributes: map[string]tfsdk.Attribute{
"attribute_example": {
Attributes: map[string]schema.Attribute{
"attribute_example": schema.XXXAttribute{
Required: bool
Optional: bool
Computed: bool
Expand Down Expand Up @@ -81,10 +81,10 @@ The following example from the `data_source_http.go` file shows how the `url` at
to be required with the Framework.
```go
func (d *httpDataSource) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"url": {
func (d *httpDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"url": schema.StringAttribute{
Required: true,
/* ... */
},
Expand Down

0 comments on commit d51781c

Please sign in to comment.