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

website: Updates for tfsdk Attribute, Block, and Schema deprecations #564

Merged
merged 3 commits into from Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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