-
Notifications
You must be signed in to change notification settings - Fork 96
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
Crashes when converting Schema #491
Comments
Seems to be Terraform 1.1 specific - with 1.2.9 this is no longer a problem... |
Hi @AndreasBergmeier6176 👋 Thank you for raising this and sorry you ran into trouble here. That stacktrace is wholly coming from Terraform CLI, e.g.
So any fixes to raise a proper error instead of a panic would need to be handled there, but the good news is that this should've been covered already when we noticed this with #326, where Terraform CLI wasn't properly checking for errors first when receiving the provider schema RPC response. Those fixes in Terraform CLI were part of hashicorp/terraform#31184 / hashicorp/terraform#31190 and should've been released with Terraform 1.2.3 according to the changelog. |
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `datasource/schema` package, which contains schema interfaces and types relevant to data sources, such as omitting plan modifiers and schema versioning. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization and validation. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for data sources. The framework design will also raise compiler-time errors for errant typing of validators. Upstream updates to terraform-plugin-framework-validators to support the new `schema/validator` package are in progress. These changes require unit testing, terraform-provider-corner testing, and website documentation updates before being ready for primetime. Now is a great time for general feedback before those efforts begin in earnest.
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `datasource/schema` package, which contains schema interfaces and types relevant to data sources, such as omitting plan modifiers and schema versioning. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization and validation. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for data sources. The framework design will also raise compiler-time errors for errant typing of validators. Upstream updates to terraform-plugin-framework-validators to support the new `schema/validator` package are in progress. These changes require unit testing, terraform-provider-corner testing, and website documentation updates before being ready for primetime. Now is a great time for general feedback before those efforts begin in earnest.
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `datasource/schema` package, which contains schema interfaces and types relevant to data sources, such as omitting plan modifiers and schema versioning. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization and validation. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for data sources. The framework design will also raise compiler-time errors for errant typing of validators.
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `datasource/schema` package, which contains schema interfaces and types relevant to data sources, such as omitting plan modifiers and schema versioning. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization and validation. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for data sources. The framework design will also raise compiler-time errors for errant typing of validators. No changes are required for data handling in the `Read` method. Example definition: ```go package test import ( "context" "github.com/bflad/terraform-plugin-framework-type-time/timetypes" "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) type ThingDataSource struct{} func (d ThingDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.LengthBetween(3, 256), }, }, "custom_string_attribute": schema.StringAttribute{ CustomType: timetypes.RFC3339Type, Optional: true, }, "list_attribute": schema.ListAttribute{ ElementType: types.StringType, Optional: true, }, "list_nested_attribute": schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ Optional: true, }, }, Validators: []validator.Object{ /*...*/ }, }, Optional: true, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, "single_nested_attribute": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "int64_attribute": schema.Int64Attribute{ Optional: true, }, }, Optional: true, }, }, Blocks: map[string]schema.Block{ "list_block": schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "float64_attribute": schema.Float64Attribute{ Optional: true, Validators: []validator.Float64{ float64validator.OneOf(1.2, 2.4), }, }, }, Validators: []validator.Object{ /*...*/ }, }, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, }, } } ``` To migrate a data source schema: - Add `github.com/hashicorp/terraform-plugin-framework/datasource/schema` to the `import` statement - Switch the `datasource.DataSource` implementation `GetSchema` method to `Schema` whose response includes a `schema.Schema` from the new package. Prior implementation: ```go func (d ThingDataSource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{/* ... */}, nil } ``` Migrated implementation: ```go func (d ThingDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{/*...*/} } ``` - Switch `map[string]tfsdk.Attribute` with `map[string]schema.Attribute` - Switch `map[string]tfsdk.Block` with `map[string]schema.Block` - Switch individual attribute and block definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the `Type` field will be removed and the map entries must declare the typed implementation, e.g. a `tfsdk.Attribute` with `Type: types.StringType` is equivalent to `schema.StringAttribute`. Custom attribute types can be specified via the `CustomType` field in each of the implementations. Prior primitive type (`types.BoolType`, `types.Float64Type`, `types.Int64Type`, `types.NumberType`, `types.StringType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.StringType, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.StringAttribute{ Required: true, } ``` Prior collection type (`types.ListType`, `types.MapType`, `types.SetType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.ListType{ ElemType: types.StringType, }, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListAttribute{ ElementType: types.StringType, Required: true, } ``` Prior single nested attributes type (`tfsdk.SingleNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{/*...*/}, Required: true, } ``` Prior collection nested attributes type (`tfsdk.ListNestedAttributes()`, `tfsdk.MapNestedAttributes()`, `tfsdk.SetNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{/*...*/}, }, Required: true, } ``` Prior collection blocks type (`tfsdk.Block`) attribute implementation: ```go // The "tfsdk.Block" could be omitted inside a map[string]tfsdk.Block tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{/*...*/}, Blocks: map[string]tfsdk.Block{/*...*/}, NestingMode: tfsdk.BlockNestingModeList, }, ``` Migrated implementation: ```go // The schema.XXXBlock must be declared inside map[string]schema.Block schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{/*...*/}, Blocks: map[string]schema.Block{/*...*/}, }, } ```
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `provider/schema` package, which contains schema interfaces and types relevant to providers, such as omitting `Computed`, plan modifiers, and schema versioning. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization and validation. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for providers. The framework design will also raise compiler-time errors for errant typing of validators. No changes are required for data handling in the `Read` method. Example definition: ```go package test import ( "context" "github.com/bflad/terraform-plugin-framework-type-time/timetypes" "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) type ExampleCloudProvider struct{} func (p ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.LengthBetween(3, 256), }, }, "custom_string_attribute": schema.StringAttribute{ CustomType: timetypes.RFC3339Type, Optional: true, }, "list_attribute": schema.ListAttribute{ ElementType: types.StringType, Optional: true, }, "list_nested_attribute": schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ Optional: true, }, }, Validators: []validator.Object{ /*...*/ }, }, Optional: true, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, "single_nested_attribute": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "int64_attribute": schema.Int64Attribute{ Optional: true, }, }, Optional: true, }, }, Blocks: map[string]schema.Block{ "list_block": schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "float64_attribute": schema.Float64Attribute{ Optional: true, Validators: []validator.Float64{ float64validator.OneOf(1.2, 2.4), }, }, }, Validators: []validator.Object{ /*...*/ }, }, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, }, } } ``` To migrate a provider schema: - Add `github.com/hashicorp/terraform-plugin-framework/provider/schema` to the `import` statement - Switch the `provider.Provider` implementation `GetSchema` method to `Schema` whose response includes a `schema.Schema` from the new package. Prior implementation: ```go func (p ExampleCloudProvider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{/* ... */}, nil } ``` Migrated implementation: ```go func (p ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{/*...*/} } ``` If the provider requires no schema, the method can be entirely empty. - Switch `map[string]tfsdk.Attribute` with `map[string]schema.Attribute` - Switch `map[string]tfsdk.Block` with `map[string]schema.Block` - Switch individual attribute and block definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the `Type` field will be removed and the map entries must declare the typed implementation, e.g. a `tfsdk.Attribute` with `Type: types.StringType` is equivalent to `schema.StringAttribute`. Custom attribute types can be specified via the `CustomType` field in each of the implementations. Prior primitive type (`types.BoolType`, `types.Float64Type`, `types.Int64Type`, `types.NumberType`, `types.StringType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.StringType, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.StringAttribute{ Required: true, } ``` Prior collection type (`types.ListType`, `types.MapType`, `types.SetType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.ListType{ ElemType: types.StringType, }, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListAttribute{ ElementType: types.StringType, Required: true, } ``` Prior single nested attributes type (`tfsdk.SingleNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{/*...*/}, Required: true, } ``` Prior collection nested attributes type (`tfsdk.ListNestedAttributes()`, `tfsdk.MapNestedAttributes()`, `tfsdk.SetNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{/*...*/}, }, Required: true, } ``` Prior collection blocks type (`tfsdk.Block`) attribute implementation: ```go // The "tfsdk.Block" could be omitted inside a map[string]tfsdk.Block tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{/*...*/}, Blocks: map[string]tfsdk.Block{/*...*/}, NestingMode: tfsdk.BlockNestingModeList, }, ``` Migrated implementation: ```go // The schema.XXXBlock must be declared inside map[string]schema.Block schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{/*...*/}, Blocks: map[string]schema.Block{/*...*/}, }, } ```
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `provider/schema` package, which contains schema interfaces and types relevant to providers, such as omitting `Computed`, plan modifiers, and schema versioning. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization and validation. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for providers. The framework design will also raise compiler-time errors for errant typing of validators. No changes are required for data handling in the `Read` method. Example definition: ```go package test import ( "context" "github.com/bflad/terraform-plugin-framework-type-time/timetypes" "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) type ExampleCloudProvider struct{} func (p ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.LengthBetween(3, 256), }, }, "custom_string_attribute": schema.StringAttribute{ CustomType: timetypes.RFC3339Type, Optional: true, }, "list_attribute": schema.ListAttribute{ ElementType: types.StringType, Optional: true, }, "list_nested_attribute": schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ Optional: true, }, }, Validators: []validator.Object{ /*...*/ }, }, Optional: true, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, "single_nested_attribute": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "int64_attribute": schema.Int64Attribute{ Optional: true, }, }, Optional: true, }, }, Blocks: map[string]schema.Block{ "list_block": schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "float64_attribute": schema.Float64Attribute{ Optional: true, Validators: []validator.Float64{ float64validator.OneOf(1.2, 2.4), }, }, }, Validators: []validator.Object{ /*...*/ }, }, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, }, } } ``` To migrate a provider schema: - Add `github.com/hashicorp/terraform-plugin-framework/provider/schema` to the `import` statement - Switch the `provider.Provider` implementation `GetSchema` method to `Schema` whose response includes a `schema.Schema` from the new package. Prior implementation: ```go func (p ExampleCloudProvider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{/* ... */}, nil } ``` Migrated implementation: ```go func (p ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{/*...*/} } ``` If the provider requires no schema, the method can be entirely empty. - Switch `map[string]tfsdk.Attribute` with `map[string]schema.Attribute` - Switch `map[string]tfsdk.Block` with `map[string]schema.Block` - Switch individual attribute and block definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the `Type` field will be removed and the map entries must declare the typed implementation, e.g. a `tfsdk.Attribute` with `Type: types.StringType` is equivalent to `schema.StringAttribute`. Custom attribute types can be specified via the `CustomType` field in each of the implementations. Prior primitive type (`types.BoolType`, `types.Float64Type`, `types.Int64Type`, `types.NumberType`, `types.StringType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.StringType, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.StringAttribute{ Required: true, } ``` Prior collection type (`types.ListType`, `types.MapType`, `types.SetType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.ListType{ ElemType: types.StringType, }, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListAttribute{ ElementType: types.StringType, Required: true, } ``` Prior single nested attributes type (`tfsdk.SingleNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{/*...*/}, Required: true, } ``` Prior collection nested attributes type (`tfsdk.ListNestedAttributes()`, `tfsdk.MapNestedAttributes()`, `tfsdk.SetNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{/*...*/}, }, Required: true, } ``` Prior collection blocks type (`tfsdk.Block`) attribute implementation: ```go // The "tfsdk.Block" could be omitted inside a map[string]tfsdk.Block tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{/*...*/}, Blocks: map[string]tfsdk.Block{/*...*/}, NestingMode: tfsdk.BlockNestingModeList, }, ``` Migrated implementation: ```go // The schema.XXXBlock must be declared inside map[string]schema.Block schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{/*...*/}, Blocks: map[string]schema.Block{/*...*/}, }, } ```
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `resource/schema` package, which contains schema interfaces and types relevant to resources. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization, plan modification, and validation. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for resources. The framework design will also raise compiler-time errors for errant typing of validators. No changes are required for data handling in any other `resource.Resource` methods. Example definition: ```go package test import ( "context" "github.com/bflad/terraform-plugin-framework-type-time/timetypes" "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) type ThingResource struct{} func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.LengthBetween(3, 256), }, }, "custom_string_attribute": schema.StringAttribute{ CustomType: timetypes.RFC3339Type, Optional: true, }, "list_attribute": schema.ListAttribute{ ElementType: types.StringType, Optional: true, }, "list_nested_attribute": schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ Optional: true, }, }, Validators: []validator.Object{ /*...*/ }, }, Optional: true, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, "single_nested_attribute": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "int64_attribute": schema.Int64Attribute{ Optional: true, }, }, Optional: true, }, }, Blocks: map[string]schema.Block{ "list_block": schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "float64_attribute": schema.Float64Attribute{ Optional: true, Validators: []validator.Float64{ float64validator.OneOf(1.2, 2.4), }, }, }, Validators: []validator.Object{ /*...*/ }, }, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, }, } } ``` To migrate a resource schema: - Add `github.com/hashicorp/terraform-plugin-framework/resource/schema` to the `import` statement - Switch the `resource.Resource` implementation `GetSchema` method to `Schema` whose response includes a `schema.Schema` from the new package. Prior implementation: ```go func (r ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{/* ... */}, nil } ``` Migrated implementation: ```go func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{/*...*/} } ``` If the resource requires no schema, the method can be entirely empty. - Switch `map[string]tfsdk.Attribute` with `map[string]schema.Attribute` - Switch `map[string]tfsdk.Block` with `map[string]schema.Block` - Switch individual attribute and block definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the `Type` field will be removed and the map entries must declare the typed implementation, e.g. a `tfsdk.Attribute` with `Type: types.StringType` is equivalent to `schema.StringAttribute`. Custom attribute types can be specified via the `CustomType` field in each of the implementations. Prior primitive type (`types.BoolType`, `types.Float64Type`, `types.Int64Type`, `types.NumberType`, `types.StringType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.StringType, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.StringAttribute{ Required: true, } ``` Prior collection type (`types.ListType`, `types.MapType`, `types.SetType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.ListType{ ElemType: types.StringType, }, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListAttribute{ ElementType: types.StringType, Required: true, } ``` Prior single nested attributes type (`tfsdk.SingleNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{/*...*/}, Required: true, } ``` Prior collection nested attributes type (`tfsdk.ListNestedAttributes()`, `tfsdk.MapNestedAttributes()`, `tfsdk.SetNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{/*...*/}, }, Required: true, } ``` Prior collection blocks type (`tfsdk.Block`) attribute implementation: ```go // The "tfsdk.Block" could be omitted inside a map[string]tfsdk.Block tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{/*...*/}, Blocks: map[string]tfsdk.Block{/*...*/}, NestingMode: tfsdk.BlockNestingModeList, }, ``` Migrated implementation: ```go // The schema.XXXBlock must be declared inside map[string]schema.Block schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{/*...*/}, Blocks: map[string]schema.Block{/*...*/}, }, } ```
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `resource/schema` package, which contains schema interfaces and types relevant to resources. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization, plan modification, and validation. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for resources. The framework design will also raise compiler-time errors for errant typing of validators. No changes are required for data handling in any other `resource.Resource` methods. Example definition: ```go package test import ( "context" "github.com/bflad/terraform-plugin-framework-type-time/timetypes" "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) type ThingResource struct{} func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.LengthBetween(3, 256), }, }, "custom_string_attribute": schema.StringAttribute{ CustomType: timetypes.RFC3339Type, Optional: true, }, "list_attribute": schema.ListAttribute{ ElementType: types.StringType, Optional: true, }, "list_nested_attribute": schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ Optional: true, }, }, Validators: []validator.Object{ /*...*/ }, }, Optional: true, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, "single_nested_attribute": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "int64_attribute": schema.Int64Attribute{ Optional: true, }, }, Optional: true, }, }, Blocks: map[string]schema.Block{ "list_block": schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "float64_attribute": schema.Float64Attribute{ Optional: true, Validators: []validator.Float64{ float64validator.OneOf(1.2, 2.4), }, }, }, Validators: []validator.Object{ /*...*/ }, }, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, }, } } ``` To migrate a resource schema: - Add `github.com/hashicorp/terraform-plugin-framework/resource/schema` to the `import` statement - Switch the `resource.Resource` implementation `GetSchema` method to `Schema` whose response includes a `schema.Schema` from the new package. Prior implementation: ```go func (r ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{/* ... */}, nil } ``` Migrated implementation: ```go func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{/*...*/} } ``` If the resource requires no schema, the method can be entirely empty. - Switch `map[string]tfsdk.Attribute` with `map[string]schema.Attribute` - Switch `map[string]tfsdk.Block` with `map[string]schema.Block` - Switch individual attribute and block definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the `Type` field will be removed and the map entries must declare the typed implementation, e.g. a `tfsdk.Attribute` with `Type: types.StringType` is equivalent to `schema.StringAttribute`. Custom attribute types can be specified via the `CustomType` field in each of the implementations. Prior primitive type (`types.BoolType`, `types.Float64Type`, `types.Int64Type`, `types.NumberType`, `types.StringType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.StringType, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.StringAttribute{ Required: true, } ``` Prior collection type (`types.ListType`, `types.MapType`, `types.SetType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.ListType{ ElemType: types.StringType, }, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListAttribute{ ElementType: types.StringType, Required: true, } ``` Prior single nested attributes type (`tfsdk.SingleNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{/*...*/}, Required: true, } ``` Prior collection nested attributes type (`tfsdk.ListNestedAttributes()`, `tfsdk.MapNestedAttributes()`, `tfsdk.SetNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{/*...*/}, }, Required: true, } ``` Prior collection blocks type (`tfsdk.Block`) attribute implementation: ```go // The "tfsdk.Block" could be omitted inside a map[string]tfsdk.Block tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{/*...*/}, Blocks: map[string]tfsdk.Block{/*...*/}, NestingMode: tfsdk.BlockNestingModeList, }, ``` Migrated implementation: ```go // The schema.XXXBlock must be declared inside map[string]schema.Block schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{/*...*/}, Blocks: map[string]schema.Block{/*...*/}, }, } ```
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `provider/metaschema` package, which contains schema interfaces and types relevant to provider meta schemas, such as omitting `Computed`, `DeprecatedMessage`, `Sensitive`, plan modifiers, validators, blocks, schema descriptions, and schema versioning. This new schema implementation also provides strongly typed attributes and nested attributes with customizable types. Nested attributes are exposed with a separate nested object for customization. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for provider meta schemas. No changes are required for data handling during requests. Example definition: ```go package test import ( "context" "github.com/bflad/terraform-plugin-framework-type-time/timetypes" "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) type ExampleCloudProvider struct{} func (p ExampleCloudProvider) MetaSchema(ctx context.Context, req provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) { resp.Schema = metaschema.Schema{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ Required: true, }, "custom_string_attribute": schema.StringAttribute{ CustomType: timetypes.RFC3339Type, Optional: true, }, "list_attribute": schema.ListAttribute{ ElementType: types.StringType, Optional: true, }, "list_nested_attribute": schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ Optional: true, }, }, }, Optional: true, }, "single_nested_attribute": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "int64_attribute": schema.Int64Attribute{ Optional: true, }, }, Optional: true, }, }, } } ``` To migrate a provider meta schema: - Add `github.com/hashicorp/terraform-plugin-framework/provider/metaschema` to the `import` statement - Switch the `provider.ProviderWithMetaSchema` implementation `GetMetaSchema` method to `MetaSchema` whose response includes a `metaschema.Schema` from the new package. Prior implementation: ```go func (p ExampleCloudProvider) GetMetaSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{/* ... */}, nil } ``` Migrated implementation: ```go func (p ExampleCloudProvider) MetaSchema(ctx context.Context, req provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) { resp.Schema = metaschema.Schema{/*...*/} } ``` If the provider requires no meta schema, the method can be removed entirely. - Switch `map[string]tfsdk.Attribute` with `map[string]metaschema.Attribute` - Switch individual attribute definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the `Type` field will be removed and the map entries must declare the typed implementation, e.g. a `tfsdk.Attribute` with `Type: types.StringType` is equivalent to `metaschema.StringAttribute`. Custom attribute types can be specified via the `CustomType` field in each of the implementations. Prior primitive type (`types.BoolType`, `types.Float64Type`, `types.Int64Type`, `types.NumberType`, `types.StringType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.StringType, } ``` Migrated implementation: ```go // The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute schema.StringAttribute{ Required: true, } ``` Prior collection type (`types.ListType`, `types.MapType`, `types.SetType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.ListType{ ElemType: types.StringType, }, } ``` Migrated implementation: ```go // The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute metaschema.ListAttribute{ ElementType: types.StringType, Required: true, } ``` Prior single nested attributes type (`tfsdk.SingleNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute metaschema.SingleNestedAttribute{ Attributes: map[string]metaschema.Attribute{/*...*/}, Required: true, } ``` Prior collection nested attributes type (`tfsdk.ListNestedAttributes()`, `tfsdk.MapNestedAttributes()`, `tfsdk.SetNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute metaschema.ListNestedAttribute{ NestedObject: metaschema.NestedAttributeObject{ Attributes: map[string]metaschema.Attribute{/*...*/}, }, Required: true, } ```
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `provider/metaschema` package, which contains schema interfaces and types relevant to provider meta schemas, such as omitting `Computed`, `DeprecatedMessage`, `Sensitive`, plan modifiers, validators, blocks, schema descriptions, and schema versioning. This new schema implementation also provides strongly typed attributes and nested attributes with customizable types. Nested attributes are exposed with a separate nested object for customization. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for provider meta schemas. No changes are required for data handling during requests. Example definition: ```go package test import ( "context" "github.com/bflad/terraform-plugin-framework-type-time/timetypes" "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) type ExampleCloudProvider struct{} func (p ExampleCloudProvider) MetaSchema(ctx context.Context, req provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) { resp.Schema = metaschema.Schema{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ Required: true, }, "custom_string_attribute": schema.StringAttribute{ CustomType: timetypes.RFC3339Type, Optional: true, }, "list_attribute": schema.ListAttribute{ ElementType: types.StringType, Optional: true, }, "list_nested_attribute": schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ Optional: true, }, }, }, Optional: true, }, "single_nested_attribute": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "int64_attribute": schema.Int64Attribute{ Optional: true, }, }, Optional: true, }, }, } } ``` To migrate a provider meta schema: - Add `github.com/hashicorp/terraform-plugin-framework/provider/metaschema` to the `import` statement - Switch the `provider.ProviderWithMetaSchema` implementation `GetMetaSchema` method to `MetaSchema` whose response includes a `metaschema.Schema` from the new package. Prior implementation: ```go func (p ExampleCloudProvider) GetMetaSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{/* ... */}, nil } ``` Migrated implementation: ```go func (p ExampleCloudProvider) MetaSchema(ctx context.Context, req provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) { resp.Schema = metaschema.Schema{/*...*/} } ``` If the provider requires no meta schema, the method can be removed entirely. - Switch `map[string]tfsdk.Attribute` with `map[string]metaschema.Attribute` - Switch individual attribute definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the `Type` field will be removed and the map entries must declare the typed implementation, e.g. a `tfsdk.Attribute` with `Type: types.StringType` is equivalent to `metaschema.StringAttribute`. Custom attribute types can be specified via the `CustomType` field in each of the implementations. Prior primitive type (`types.BoolType`, `types.Float64Type`, `types.Int64Type`, `types.NumberType`, `types.StringType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.StringType, } ``` Migrated implementation: ```go // The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute schema.StringAttribute{ Required: true, } ``` Prior collection type (`types.ListType`, `types.MapType`, `types.SetType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.ListType{ ElemType: types.StringType, }, } ``` Migrated implementation: ```go // The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute metaschema.ListAttribute{ ElementType: types.StringType, Required: true, } ``` Prior single nested attributes type (`tfsdk.SingleNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute metaschema.SingleNestedAttribute{ Attributes: map[string]metaschema.Attribute{/*...*/}, Required: true, } ``` Prior collection nested attributes type (`tfsdk.ListNestedAttributes()`, `tfsdk.MapNestedAttributes()`, `tfsdk.SetNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute metaschema.ListNestedAttribute{ NestedObject: metaschema.NestedAttributeObject{ Attributes: map[string]metaschema.Attribute{/*...*/}, }, Required: true, } ```
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. |
Module version
Relevant provider source code
Seems to be triggered by:
Terraform Configuration Files
Debug Output
Expected Behavior
Should produce an error but not crash.
Actual Behavior
Crashes with:
Steps to Reproduce
References
The text was updated successfully, but these errors were encountered: