From fe9d1f2d4e1c007a0b800a1dc48eb6f68030f3ea Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 25 Aug 2022 17:38:45 +0100 Subject: [PATCH 01/28] Adding docs for migrating from SDKv2 to the Framework (#459) --- website/data/plugin-framework-nav-data.json | 91 +++++ .../attributes-blocks/attribute-schema.mdx | 129 ++++++ .../attributes-blocks/blocks-computed.mdx | 107 +++++ .../migrating/attributes-blocks/blocks.mdx | 144 +++++++ .../attributes-blocks/default-values.mdx | 147 +++++++ .../migrating/attributes-blocks/fields.mdx | 90 +++++ .../migrating/attributes-blocks/force-new.mdx | 110 +++++ .../migrating/attributes-blocks/types.mdx | 97 +++++ .../attributes-blocks/validators-custom.mdx | 163 ++++++++ .../validators-predefined.mdx | 164 ++++++++ .../framework/migrating/data-sources.mdx | 180 +++++++++ .../docs/plugin/framework/migrating/index.mdx | 50 +++ .../plugin/framework/migrating/provider.mdx | 378 ++++++++++++++++++ .../framework/migrating/resources/crud.mdx | 192 +++++++++ .../framework/migrating/resources/import.mdx | 173 ++++++++ .../migrating/resources/plan-modification.mdx | 191 +++++++++ .../framework/migrating/resources/schema.mdx | 224 +++++++++++ .../migrating/resources/state-upgrade.mdx | 207 ++++++++++ .../plugin/framework/migrating/schema.mdx | 142 +++++++ .../plugin/framework/migrating/testing.mdx | 170 ++++++++ 20 files changed, 3149 insertions(+) create mode 100644 website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx create mode 100644 website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx create mode 100644 website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx create mode 100644 website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx create mode 100644 website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx create mode 100644 website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx create mode 100644 website/docs/plugin/framework/migrating/attributes-blocks/types.mdx create mode 100644 website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx create mode 100644 website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx create mode 100644 website/docs/plugin/framework/migrating/data-sources.mdx create mode 100644 website/docs/plugin/framework/migrating/index.mdx create mode 100644 website/docs/plugin/framework/migrating/provider.mdx create mode 100644 website/docs/plugin/framework/migrating/resources/crud.mdx create mode 100644 website/docs/plugin/framework/migrating/resources/import.mdx create mode 100644 website/docs/plugin/framework/migrating/resources/plan-modification.mdx create mode 100644 website/docs/plugin/framework/migrating/resources/schema.mdx create mode 100644 website/docs/plugin/framework/migrating/resources/state-upgrade.mdx create mode 100644 website/docs/plugin/framework/migrating/schema.mdx create mode 100644 website/docs/plugin/framework/migrating/testing.mdx diff --git a/website/data/plugin-framework-nav-data.json b/website/data/plugin-framework-nav-data.json index f707f639c..d15c481de 100644 --- a/website/data/plugin-framework-nav-data.json +++ b/website/data/plugin-framework-nav-data.json @@ -79,5 +79,96 @@ { "title": "Debugging", "path": "debugging" + }, + { + "title": "Migrating", + "routes": [ + { + "title": "Overview", + "path": "migrating" + }, + { + "title": "Testing", + "path": "migrating/testing" + }, + { + "title": "Schema", + "path": "migrating/schema" + }, + { + "title": "Provider", + "path": "migrating/provider" + }, + { + "title": "Resources", + "routes": [ + { + "title": "Schema", + "path": "migrating/resources/schema" + }, + { + "title": "CRUD Functions", + "path": "migrating/resources/crud" + }, + { + "title": "Import", + "path": "migrating/resources/import" + }, + { + "title": "Plan Modification", + "path": "migrating/resources/plan-modification" + }, + { + "title": "State Upgraders", + "path": "migrating/resources/state-upgrade" + } + ] + }, + { + "title": "Data Sources", + "path": "migrating/data-sources" + }, + { + "title": "Attributes & Blocks", + "routes": [ + { + "title": "Attribute Schema", + "path": "migrating/attributes-blocks/attribute-schema" + }, + { + "title": "Attribute Types", + "path": "migrating/attributes-blocks/types" + }, + { + "title": "Attribute Fields", + "path": "migrating/attributes-blocks/fields" + }, + { + "title": "Default Values", + "path": "migrating/attributes-blocks/default-values" + }, + { + "title": "Force New", + "path": "migrating/attributes-blocks/force-new" + }, + { + "title": "Validators - Predefined", + "path": "migrating/attributes-blocks/validators-predefined" + }, + { + "title": "Validators - Custom", + "path": "migrating/attributes-blocks/validators-custom" + }, + { + "title": "Blocks", + "path": "migrating/attributes-blocks/blocks" + }, + { + "title": "Blocks with Computed Fields", + "path": "migrating/attributes-blocks/blocks-computed" + } + ] + } + ] } ] diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx new file mode 100644 index 000000000..c2190438b --- /dev/null +++ b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx @@ -0,0 +1,129 @@ +--- +page_title: 'Attribute Schema: Migrating from SDKv2 to the Framework' +description: >- + Migrate attributes from SDKv2 to the plugin Framework +--- + +# Attribute Schema + +Attributes define how users can configure values for your Terraform provider, resources, and data sources. Refer to +[Schemas - Attributes](/plugin/framework/schemas#attributes) in the Framework documentation for details. + +This page explains how to migrate an attribute from SDKv2 to the plugin Framework. + +## SDKv2 +In SDKv2, attributes are defined by the `Schema` field in the provider, resource, or data source schema. The `Schema` +field maps each attribute name (string) to the attribute's `schema.Schema` struct. Both resources and data sources are +defined using the `schema.Resource` struct. + +The following code shows a basic implementation of attribute schema for a provider in SDKv2. For clarity, the body of +the attribute's schema is omitted (denoted by `...`). + +```go +func ProviderExample() *schema.Provider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + ... +}, +``` + +In SDKv2, resource and data source attributes are defined the same way on their respective types. + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + ... + }, +``` +## 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. + +The following code shows how to define an attribute for a resource with the Framework. For clarity, the body of the +`tfsdk.Attribute` struct is omitted (denoted by `...`). + +```go +func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.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. +- 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. + +## Example + +The following examples show how to migrate portions of the +[http](https://github.com/hashicorp/terraform-provider-http) provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-http` repository and compare the `data_source.go` file in +[v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source.go) +and the `data_source_http.go` file in +[v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http.go). + + +**SDKv2** + +The following example from the `data_source.go` file shows the implementation of the `url` attribute for the `http` +data source. + +```go +func dataSource() *schema.Resource { + return &schema.Resource{ + ... + Schema: map[string]*schema.Schema{ + "url": { + Description: "The URL for the request. Supported schemes are `http` and `https`.", + Type: schema.TypeString, + Required: true, + }, +``` + + +**Framework** + +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 *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + "url": { + Description: "The URL for the request. Supported schemes are `http` and `https`.", + Type: types.StringType, + Required: true, + }, + ... +``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx new file mode 100644 index 000000000..8f32e4aab --- /dev/null +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -0,0 +1,107 @@ +--- +page_title: 'Computed Blocks: Migrating from SDKv2 to the Framework' +description: >- + Migrate computed blocks from SDKv2 to attribute validators in the plugin Framework. +--- + +# Blocks with Computed Fields + +Some providers, resources, and data sources include repeatable nested blocks in their attributes. Some blocks contain +fields with `Computed: true`, which means that the provider code can define the value or that it could come from the +output of terraform apply (e.g., the ID of an EC2 instance). Refer to [Schemas - Blocks](/plugin/framework/FIXME) in the +Framework documentation for details. + +This page explains how to migrate computed blocks from SDKv2 to the Framework. Refer to +[Blocks](/plugin/framework/migrating/attributes-blocks/blocks) if you are looking for information about migrating blocks +that do not contain computed fields. + +## SDKv2 + +In SDKv2, blocks are defined by an attribute whose type is `TypeList`, or `TypeSet` and whose `Elem` field is set to a +`schema.Resource` that contains a map of the block's attribute names to corresponding `schemaSchema` structs. + +```go +map[string]*schema.Schema{ + "example": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "nested_example": { + Type: schema.TypeString, + Computed: true, + ... +``` + +## 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`. + +When working with protocol version 6, it is recommended that computed blocks are represented using nested attributes. + +```go +map[string]tfsdk.Attribute{ +"example": { + Computed: true, + Type: types.ListType{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_example": types.StringType, + ... + +``` +## 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 6 used [nested attributes](https://www.terraform.io/plugin/framework/schemas#attributes-1) when migrating blocks that are computed from SDKv2 to Framework. + +## Example + +The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) +provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-tls` repository and compare the `data_source_certificate.go` file in +[v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/data_source_certificate.go) +with +[v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/data_source_certificate.go). + +**SDKv2** + +The following example from the `data_source_certificate.go` file shows the implementation of the `certificates` nested +block on the `certificate` data source's schema. + +```go +Schema: map[string]*schema.Schema{ +"certificates": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "signature_algorithm": { + Type: schema.TypeString, + Computed: true, + ... + }, +``` + +**Framework** + +The following shows the same section of provider code after the migration. + +This code defines the `certificates` block as an attribute on the `certificate` data source's schema, where the +`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, + }, + }, + Computed: true, + ... +``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx new file mode 100644 index 000000000..7729dbc6f --- /dev/null +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx @@ -0,0 +1,144 @@ +--- +page_title: 'Blocks: Migrating from SDKv2 to the Framework' +description: >- + Migrate blocks from SDKv2 to attribute validators in the plugin Framework. +--- + +# Blocks + +Some providers, resources, and data sources include repeatable nested blocks in their attributes. These nested blocks +typically represent separate objects that are related to (or embedded within) the containing object. Refer to +[Schemas - Blocks](/plugin/framework/FIXME) in the Framework documentation for details. + +This page explains how to migrate nested blocks that are not computed (i.e., do not contain fields with +`Computed: true`) from SDKv2 to the Framework. Refer to +[Computed Blocks](/plugin/framework/migrating/attributes-blocks/blocks-computed) if you are looking for information +about migrating nested blocks that contain fields that are computed. + +## Nested Block Example + +The following example shows an example of a nested block in Terraform resource configuration. The `subject` nested +block within the `tls_cert_request` resource configures the subject of a certificate request with the `common_name` and +`organization` attributes. + +```hcl +resource "tls_cert_request" "example" { + private_key_pem = file("private_key.pem") + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } +} +``` + + +## SDKv2 + +In SDKv2, blocks are defined by an attribute whose type is `TypeList`, or `TypeSet` and whose `Elem` field is set to a +`schema.Resource` that contains a map of the block's attribute names to corresponding `schemaSchema` structs. + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + ... + map[string]*schema.Schema{ + "example" = &schema.Schema{ + Type: schema.TypeList, + Optional: bool, + MaxItems: int, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "nested_example": { + Type: schema.TypeString, + Optional: bool, + ... +``` + +## 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. + +```go +func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + ... + Blocks: map[string]tfsdk.Block{ + "example": { + NestingMode: tfsdk.BlockNestingModeList, + MaxItems: int, + Attributes: map[string]tfsdk.Attribute{ + "nested_example": { + Type: types.StringType, + Optional: bool + ... +``` + + +## Example + +The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) +provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-tls` repository and compare the `common_cert.go` file in +[v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/common_cert.go) +with the `resource_cert_request.go` file in +[v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/resource_cert_request.go). + +**SDKv2** + +The following example from the `common_cert.go` file shows the implementation of the `subject` nested block on the +`cert_request` resource's schema with SDKv2. + +```go +map[string]*schema.Schema{ + "private_key_pem": &schema.Schema{ + Type: schema.TypeString, + ... + + "subject" = &schema.Schema{ + Type: schema.TypeList, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "organization": { + Type: schema.TypeString, + ... + }, + "common_name": { + Type: schema.TypeString, + ... + }, + ... +``` + +**Framework** + +The following example from the `resource_cert_request.go` file shows how the `subject` nested `subject` block on the +`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, + ... + }, +``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx new file mode 100644 index 000000000..bae0c6e27 --- /dev/null +++ b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx @@ -0,0 +1,147 @@ +--- +page_title: 'Attribute default values: Migrating from SDKv2 to the Framework' +description: >- + Specify a default when the Terraform configuration does not supply a value for attributes. + Migrate attribute defaults in SDKv2 to AttributePlanModifier in the Framework. +--- + +# Default Values + +Default values set a value for an attribute when the Terraform configuration does not provide one. In SDKv2, default +values are set via fields on an attribute's schema. In the Framework, you set default values via plan modification. +Refer to +[Plan Modification - Attribute Plan Modification](/plugin/framework/resources/plan-modification#attribute-plan-modification) +in the Framework documentation for details. + +This page explains how to migrate attribute defaults in SDKv2 to `AttributePlanModifier` in the Framework. + +## SDKv2 + +In SDKv2, default values are defined for a primitive attribute type (i.e., `TypeBool`, `TypeFloat`, `TypeInt`, +`TypeString`) by the `Default` field on the attribute's schema. Alternatively, the `DefaultFunc` function is used to +compute a default value for an attribute. + +The following code shows a basic implementation of a default value for a primitive attribute type in SDKv2. For clarity, +the body of the attribute's schema is omitted (denoted by `...`). + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + ... + Schema: map[string]*schema.Schema{ + "attribute_example": { + Default: 2048, + ... + }, + ... +``` + +## Framework + +In the Framework, you set default values with the `AttributePlanModifier` field on your attribute's `tfsdk.Attribute` +struct. + +```go +func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { + PlanModifiers: []tfsdk.AttributePlanModifier{ + defaultValue(types.Bool{Value: true}), + ... +``` + +## Migration Notes + +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- In SDKv2, default values are set with the `Default` or `DefaultFunc` fields on an attribute's `schema.Schema` struct. +In the Framework, you must implement an attribute plan modifier to set default values. + +## Example + +The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) +provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-tls` repository and compare the `resource_private_key.go` file in +[v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/resource_private_key.go) with +[v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/resource_private_key.go). + +**SDKv2** + +The following example from the `resource_private_key.go` file shows the implementation of the `Default` field for the +`rsa_bits` attribute on the `tls_private_key` resource with SDKv2. + +```go +func resourcePrivateKey() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rsa_bits": { + Default: 2048, + ... + }, + ... +``` + +**Framework** + +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 (rt *privateKeyResourceType) 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.Int64{Value: 2048}), + ... + }, + ... + }, + ... + }, + }, nil +} +``` + +The following example from the `attribute_plan_modifiers.go` file implements the `DefaultValue` attribute plan modifier +that the `rsa_bits` attribute uses. + +```go +func DefaultValue(v attr.Value) tfsdk.AttributePlanModifier { + return &defaultValueAttributePlanModifier{v} +} + +type defaultValueAttributePlanModifier struct { + DefaultValue attr.Value +} + +var _ tfsdk.AttributePlanModifier = (*defaultValueAttributePlanModifier)(nil) + +func (apm *defaultValueAttributePlanModifier) Description(ctx context.Context) string { + ... +} + +func (apm *defaultValueAttributePlanModifier) MarkdownDescription(ctx context.Context) string { + ... +} + +func (apm *defaultValueAttributePlanModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, res *tfsdk.ModifyAttributePlanResponse) { + // If the attribute configuration is not null, we are done here + if !req.AttributeConfig.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() { + return + } + + res.AttributePlan = apm.DefaultValue +} +``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx new file mode 100644 index 000000000..8d797413d --- /dev/null +++ b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx @@ -0,0 +1,90 @@ +--- +page_title: 'Attributes flags: Migrating from SDKv2 to the Framework' +description: >- + Migrate attribute required, optional, computed and sensitive from SDKv2 to the plugin Framework +--- + +# Attribute Fields + +A subset of attribute fields, such as required, optional, computed, or sensitive, define attribute behaviour. Refer to +[Schemas - Attributes](/plugin/framework/schemas#required) in the Framework documentation for details. + +This page explains how to migrate the required, optional, computed, and sensitive attribute fields from SDKv2 to the +Framework. + +## SDKv2 + +In SDKv2, `Required`, `Optional`, `Computed`, and `Sensitive` are boolean fields on the attribute's schema. + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + ... + Schema: map[string]*schema.Schema{ + "attribute_example": { + Required bool + Optional bool + Computed bool + Sensitive bool + ... +``` + +## Framework + +In the Framework, you set the same fields on the `tfsdk.Attribute` struct, with the same behavior. + +```go +func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { + Required bool + Optional bool + Computed bool + Sensitive bool + ... +``` + +## Example + +The following examples show how to migrate portions of the [http](https://github.com/hashicorp/terraform-provider-http) +provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-http` repository and compare the `data_source.go` file in +[v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source.go) +and the `data_source_http.go` file in +[v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http.go). + +**SDKv2** + +The following example from the `data_source.go` file shows how the `url` attribute on the `http` data source is set to +be required with SDKv2. + +```go +func dataSource() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + Required: true, + ... + }, + ... +``` + +**Framework** + +The following example from the `data_source_http.go` file shows how the `url` attribute on the `http` data source is set +to be required with the Framework. + +```go +func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "url": { + Required: true, + ... + }, + ... +``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx new file mode 100644 index 000000000..5d664934b --- /dev/null +++ b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx @@ -0,0 +1,110 @@ +--- +page_title: 'Attribute ForceNew triggers: Migrating from SDKv2 to the Framework' +description: >- + Migrate attribute force new in SDKv2 to attribute plan modifier in the Framework. +--- + +# ForceNew + +In Terraform, sometimes a resource must be replaced when the value of an attribute changes. In SDKv2, this is +accomplished via the `ForceNew` field. In the Framework, you implement the same behavior via a `RequiresReplace` plan +modifier. Refer to +[Plan Modification - Attribute Plan Modification](/plugin/framework/resources/plan-modification#attribute-plan-modification) +in the Framework documentation for details. + +This page explains how to migrate this behavior from SDKv2 to the Framework. + +## SDKv2 + +In SDKv2, setting the `ForceNew` field on an attribute's `schema.Schema` triggers a replace (i.e., a destroy-create +cycle) whenever the attribute's value is changed. + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + ... + Schema: map[string]*schema.Schema{ + "attribute_example": { + ForceNew true + ... +``` + +## Framework + +In the Framework, you implement the same behavior by using the `resource.RequiresReplace` plan modifier on your +attribute's `tfsdk.Attribute` struct. + +```go +func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + ... +``` + +## Migration Notes + +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- In both SDKv2 and Framework, `ForceNew` and `RequiresReplace`, respectively, only trigger a replace if the attribute +is not computed. In the Framework, if an attribute which is computed requires that the resource is replaced when it is +changed, implement a plan modifier that triggers the replacement. Refer to +[RequiresReplacePlanModifier](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/planmodifiers/attribute.go#L63) +for an example, but bear in mind that each implementation requires different logic and you may need to detect whether +the plan has already been modified. + +## Example + +The following examples show how to migrate portions of the +[random](https://github.com/hashicorp/terraform-provider-random) provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-random-provider` repository and compare the `resource_password.go` file in +[v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) +with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). + +**SDKv2** + +The following example from the `resource_password.go` file shows the implementation of the `ForceNew` field of the +`random_password` resource's `keepers` attribute with SDKv2. + +```go +func resourcePassword() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "keepers": { + ForceNew: true, + ... + }, + ... + }, + ... + } +} +``` + +**Framework** + +The following shows the same section of provider code after the migration. + +This code forces the replacement of a `random_password` resource when the value of the `keepers` attribute is changed. +The example does this using the `PlanModifiers` field within the `random_password` attribute's schema. + +```go +func (r *passwordResourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "keepers": { + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + ... + }, + ... + }, + }, nil +} +``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx new file mode 100644 index 000000000..bf9bc01bc --- /dev/null +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -0,0 +1,97 @@ +--- +page_title: 'Attribute Types: Migrating from SDKv2 to the Framework' +description: >- + Migrate attribute type from SDKv2 to the plugin Framework +--- + +# Attribute Types + +An attribute either contains a primitive type, such as an integer or a string, or contains other attributes. Attributes +that contain other attributes are referred to as nested attributes and are implemented by populating the +`NestedAttributes` field on the `tfsdk.Attribute` struct. Refer to +[Schemas - Attributes](/plugin/framework/schemas#type) in the Framework documentation for details. + +This page explains how to migrate a primitive attribute from SDKv2 to the plugin Framework. There is an example of +migrating a nested block to a nested attribute in +[Provider Schema](/plugin/framework/migrating/provider#example-1) + +## SDKv2 + +In SDKv2, attribute types are defined by the `Type` field on the attribute's `schema.Schema` struct. + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + ... + Schema: map[string]*schema.Schema{ + "attribute_example": { + Type: schema.TypeName, + ... + }, + ... +``` +## Framework + +In the Framework, you set your attribute's type with the `Type` field on your attribute's `tfsdk.Attribute` struct. + +```go +func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { + Type: types.NameType, + ... + }, +``` +## Migration Notes +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- In the Framework, an `Attribute` struct has either the `Type` or `Attributes` field defined. The `Type` field is set to +a primitive type such as an integer or string, and you use the `Attributes` field for `NestedAttributes`. Refer to +[Provider Schema](/plugin/framework/migrating/provider#example-1) for an example of using a single +nested attribute. Nested attributes are also described in more detail in the +[docs](https://www.terraform.io/plugin/framework/schemas#attributes-1). + +## Example + +The following examples show how to migrate portions of the +[http](https://github.com/hashicorp/terraform-provider-http) provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-http` repository and compare the `data_source.go` file in +[v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source.go) +and the `data_source_http.go` file in +[v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http.go). + +**SDKv2** + +The following example from the `data_source.go` file shows the implementation of the type field of the `url` attribute +for the `http` data source with SDKv2. + +```go +func dataSource() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + Type: schema.TypeString, + ... + }, + ... +``` + +**Framework** + +The following example from the `data_source_http.go` file shows how the type of the `url` attribute for the `http` data +source is defined with the Framework after the migration. + +```go +func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "url": { + Type: types.StringType, + ... + }, + ... +``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx new file mode 100644 index 000000000..061e2dd03 --- /dev/null +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx @@ -0,0 +1,163 @@ +--- +page_title: 'Attribute Custom Validators: Migrating from SDKv2 to the Framework' +description: >- + Validations check for required syntax, types, and acceptable values. Migrate custom attribute validation functions from SDKv2 to attribute validators in the Framework. +--- + +# Custom Validators + +You can write custom validations that give users feedback about required syntax, types, and acceptable values in your +provider. The Framework has a collection of +[predefined validators](https://github.com/hashicorp/terraform-plugin-framework-validators). Refer to +[Predefined Validators](/plugin/framework/migrating/validators-predefined) to learn how to use them. + +This page explains how to migrate attribute validation functions from SDKv2 to attribute validators in the Framework. + +## SDKv2 + +In SDKv2, arbitrary validation logic can be applied to individual attributes by using `ValidateFunc` and/or +`ValidateDiagFunc`. + +The following example shows the implementation of a validation that ensures that an integer attribute has a value +greater than one. + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + ... + Schema: map[string]*schema.Schema{ + "attribute_example": { + ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)), + ... +``` + +## Framework + +In the Framework, you implement either type of validation by setting the `Validators` field on the `tfsdk.Attribute` +struct with types that satisfy the `tfsdk.AttributeValidator` interface. + +The following example shows the implementation of a validation that ensures that an integer attribute has a value +greater than one. + +```go +func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { + Validators: []tfsdk.AttributeValidator{ + int64validator.AtLeast(1), + ... +``` + +## Migration Notes + +Remember the following details when migrating from SDKv2 to the Framework. + +- In SDKv2, `ValidateDiagFunc` is a field on `schema.Schema` that you can use to define custom validation functions. In +the Framework, `Validators` is a field on each `tfsdk.Attribute` struct that can be used for custom validations. +- Use [predefined validators](/plugin/framework/migrating/validators-predefined) when there is a validator that meets +your requirements. + +## Example + +The following examples show how to migrate portions of the +[random](https://github.com/hashicorp/terraform-provider-random) provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-random` repository and compare the `resource_password.go` file in +[v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) +with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). + +**SDKv2** + +The following example from the `resource_password.go` file shows the implementation of the `ValidateDiagFunc` field for +the `random_password`'s `length` attribute to validate that it's value is at least 1 (greater than zero). + +```go +func resourcePassword() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "length": { + ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)), + }, + }, + } +} +``` + +**Framework** + +The following shows the same section of provider code after the migration. + +This code validates that the `random_password`'s `length` attribute is greater than zero by using a custom `AtLeast` +validator. + +```go +func (r *passwordResourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "length": { + Validators: []tfsdk.AttributeValidator{ + int64validator.AtLeast(1), + }, + }, + }, + }, nil +} +``` + +This example code is taken from +[terraform-plugin-framework-validators](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/v0.4.0/int64validator/at_least.go) +to illustrate how you can implement your own validators. + +```go +var _ tfsdk.AttributeValidator = atLeastValidator{} + +// atLeastValidator validates that an integer Attribute's value is at least a certain value. +type atLeastValidator struct { + min int64 +} + +// Description describes the validation in plain text formatting. +func (validator atLeastValidator) Description(_ context.Context) string { + return fmt.Sprintf("value must be at least %d", validator.min) +} + +// MarkdownDescription describes the validation in Markdown formatting. +func (validator atLeastValidator) MarkdownDescription(ctx context.Context) string { + return validator.Description(ctx) +} + +// Validate performs the validation. +func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) { + i, ok := validateInt(ctx, request, response) + + if !ok { + return + } + + if i < validator.min { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.AttributePath, + validator.Description(ctx), + fmt.Sprintf("%d", i), + )) + + return + } +} + +// AtLeast returns an AttributeValidator which ensures that any configured +// attribute value: +// +// - Is a number, which can be represented by a 64-bit integer. +// - Is exclusively greater than the given minimum. +// +// Null (unconfigured) and unknown (known after apply) values are skipped. +func AtLeast(min int64) tfsdk.AttributeValidator { + return atLeastValidator{ + min: min, + } +} +``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx new file mode 100644 index 000000000..2d54dc604 --- /dev/null +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -0,0 +1,164 @@ +--- +page_title: 'Attribute predefined validators: Migrating from SDKv2 to the Framework' +description: >- + Validations check required syntax, types, and acceptable values. + Migrate the predefined ConflictsWith, ExactlyOneOf, AtLeastOneOf and RequiredWith validators to the Framework. +--- + +# Validators - Predefined + +Attribute validators ensure that attributes do or do not contain specific values. You can use predefined validators for +many use cases, or implement custom validators. Refer to [Schemas - Validators](/plugin/framework/schemas#validators) in +the Framework documentation for details. Refer to the +[Attributes - Custom Validators](/plugin/framework/migrating/validators-custom) page in this guide to learn how to +implement custom validators. + +This page explains how to migrate a predefined validator from SDKv2 to the Framework. + +## SDKv2 + +In SDKv2, the `ConflictsWith`, `ExactlyOneOf`, `AtLeastOneOf`, and `RequiredWith` fields on an attribute's +`schema.Schema` struct perform predefined validations on the list of attributes set for these fields. + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + ... + Schema: map[string]*schema.Schema{ + "attribute_example": { + ConflictsWith: []string + ExactlyOneOf: []string + AtLeastOneOf: []string + RequiredWith: []string +... +``` +## Framework + +In the Framework, you implement either type of validation by setting the `Validators` field on the `tfsdk.Attribute` +struct with types that satisfy the `tfsdk.AttributeValidator` interface. Validators that perform the same checks as the +predefined validators in SDKv2 are +[available for the Framework](https://github.com/hashicorp/terraform-plugin-framework-validators). If the predefined +validators do not meet your needs, you must define +[custom validators](/plugin/framework/migrating/validators-custom). + +```go +func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { + Validators: []tfsdk.AttributeValidator{ + schemavalidator.ConflictsWith(), + ... +``` + +## Migration Notes + +Remember the following details when migrating from SDKv2 to the Framework. + +- In SDKv2, `ValidateDiagFunc` is a field on `schema.Schema` that you can use to define validation functions. In SDKv2, +there are also built-in validations. For example, `ConflictsWith` is a field on the `schema.Schema` struct in SDKv2. In +the Framework, `Validators` is a field on each `tfsdk.Attribute` struct. +- Validators replicating the behavior of `ConflictsWith`, `ExactlyOneOf`, `AtLeastOneOf` and `RequiredWith` in SDKv2 are +available for the Framework: + - [ConflictsWith](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/v0.4.0/schemavalidator/conflicts_with.go) + - [ExactlyOneOf](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/v0.4.0/schemavalidator/exactly_one_of.go) + - [AtLeastOneOf](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/v0.4.0/schemavalidator/at_least_one_of.go) + - [AlsoRequires](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/v0.4.0/schemavalidator/also_requires.go) +- Define [custom validators](/plugin/framework/migrating/validators-custom) when the predefined validators do not meet +your requirements. + +## Example + +The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) +provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-tls` repository and compare the `provider.go` file in +[v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/provider.go) +with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/provider.go). + +### SDKv2 + +The following example from the `provider.go` file shows the implementation of the `ConflictsWith` field on the +provider's `proxy` block's `url` attribute. This validator checks that the provider does not use the `url` attribute +when the proxy's url is set through the environment. The example also uses the `RequiredWith` field to ensure that the +`password` attribute is configured when `username` is, and vice-versa. + +```go +func New() (*schema.Provider, error) { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "proxy": { + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + ConflictsWith: []string{"proxy.0.from_env"}, + ... + }, + "username": { + RequiredWith: []string{"proxy.0.url"}, + ... + }, + "password": { + RequiredWith: []string{"proxy.0.username"}, + ... + }, + "from_env": { + ConflictsWith: []string{"proxy.0.url", "proxy.0.username", "proxy.0.password"}, + ... + }, + }, + }, + }, + }, + }, nil +} +``` + +### Framework + +The following shows the same section of provider code after the migration. + +This code implements the `ConflictsWith` and `AlsoRequires` validators with the Framework. The validators are configured +via the `Validators` field of the provider's `proxy` block's attribute schema. + +```go +func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Blocks: map[string]tfsdk.Block{ + "proxy": { + Attributes: map[string]tfsdk.Attribute{ + "url": { + Validators: []tfsdk.AttributeValidator{ + schemavalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), + }, + ... + }, + "username": { + Validators: []tfsdk.AttributeValidator{ + schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("url")), + }, + ... + }, + "password": { + Validators: []tfsdk.AttributeValidator{ + schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("username")), + }, + ... + }, + "from_env": { + Validators: []tfsdk.AttributeValidator{ + schemavalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("url"), + path.MatchRelative().AtParent().AtName("username"), + path.MatchRelative().AtParent().AtName("password"), + ), + }, + ... + }, + }, + }, + }, nil +} +``` diff --git a/website/docs/plugin/framework/migrating/data-sources.mdx b/website/docs/plugin/framework/migrating/data-sources.mdx new file mode 100644 index 000000000..9414f6d14 --- /dev/null +++ b/website/docs/plugin/framework/migrating/data-sources.mdx @@ -0,0 +1,180 @@ +--- +page_title: 'Data Sources: Migrating from SDKv2 to the Framework' +description: >- + Migrate a data source from SDKv2 to the plugin Framework. +--- + +# Data Sources + +Data sources let Terraform reference external data. Unlike resources, Terraform does not create, update, or delete +data sources, and makes no attempt to modify the underlying API. Data Sources are a read-only resource type, so they +only implement a subset of the operations that resources do. Refer to [Data Sources](/plugin/framework/data-sources) +in the Framework documentation for details. + +This page explains how to migrate a data source from SDKv2 to the plugin Framework. + +## SDKv2 + +In SDKv2, data sources are defined by the `DataSourcesMap` field on the `schema.Provider` struct, which maps data source +names (strings) to their schema. The `schema.Resource` struct is used for both resources and data sources. + +The following example shows a typical implementation. The rest of the `schema.Provider` struct is omitted for clarity +(denoted by `...`). + +```go +func New() *schema.Provider { + return &schema.Provider{ + DataSourcesMap: map[string]*schema.Resource{} + ... +}, +``` + +In SDKv2, you define both resources and data sources with `schema.Resource` structs. The following example shows a +resource struct. For clarity, the example omits fields that are not available for data sources. + +```go +schema.Resource { + Schema: map[string]*schema.Schema, + Read: ReadFunc, + ReadContext: ReadContextFunc, + ReadWithoutTimeout: ReadContextFunc, + DeprecationMessage: string, + Timeouts: *ResourceTimeout, + Description: string, +} + +``` + +## Framework + +In the Framework, you define your provider's data sources adding them to your provider's `GetDataSources` function. + +The `GetDataSources` function on your `provider.Provider` returns a map from the data source name (string) to a struct +that implements the `DataSourceType` interface for each data source your provider supports. + +The following code shows how you add a data source to your provider with the Framework. + +```go +func (p *provider) GetDataSources(ctx context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { + return map[string]provider.ResourceType{}, nil +} +``` + +Like the `provider.ResourceType` interface, `provider.DataSourceType` requires `GetSchema` and `NewResource` functions. +These functions work the same way for data sources as they do for resources. + +The `GetSchema` function returns a `tfsdk.Schema` struct which defines your data source's attributes. This is the same +struct you use to define provider and resource attributes. + +The `NewResource` function returns a type that you define. The type implements the `resource.Resource` interface, +including the CRUD functions for your resource. + +The following code shows how you define a `provider.DataSourceType` which implements these two functions with the +Framework. + +```go +type dataSourceTypeExample struct{} + +func (r *dataSourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + ... +} + +func (r *dataSourceTypeExample) NewDataSource(ctx context.Context, p provider.Provider) (datasource.DataSource, diag.Diagnostics) { + ... +} +``` + +Unlike resources, you only need to implement a read function for your data sources. Refer to the +[Resources - CRUD functions](/plugin/framework/migrating/resources/crud) page in this guide to learn how to define this +function on your `datasource.DataSource` type. + +## Migration Notes + +Remember the following details when completing the migration from SDKv2 to the Framework. + +- As data sources are read-only, you only implement read functionality for your provider's data sources. Refer to the +[`Read` function](/plugin/framework/resources#read) in the Framework documentation for resources for more details. + +## Example + +The following examples show how to migrate portions of the [http](https://github.com/hashicorp/terraform-provider-http) +provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-http` repository and compare the `data_source.go` file in +[v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source.go) +and the `data_source_http.go` file in +[v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http.go). + +**SDKv2** + +The following example from the `provider.go` file shows an implementation of the `DataSourcesMap` field on the provider +schema with SDKv2. + +```go +func New() (*schema.Provider, error) { + return &schema.Provider { + DataSourcesMap: map[string]*schema.Resource { + "http": dataSource(), + ... +``` + +The following example from the `data_source.go` file shows how the `ReadContext` function and `Schema` are defined for +the `http` data source with SDKv2. + +```go +func dataSource() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceRead, + + Schema: map[string]*schema.Schema{ + "url": { + Description: "The URL for the request. Supported schemes are `http` and `https`.", + Type: schema.TypeString, + Required: true, + }, + ... + }, + } +} +``` + +**Framework** + +The following example from the `provider.go` file shows how the `http` data source is defined with the Framework after +the migration. + +```go +func (p *provider) GetDataSources(context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { + return map[string]provider.DataSourceType{ + "http": &httpDataSourceType{}, + }, nil +} +``` + +This code from the `data_source_http.go` file defines the `GetSchema` function for the `http` data source with the +Framework. + +```go +func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "url": { + Description: "The URL for the request. Supported schemes are `http` and `https`.", + Type: types.StringType, + Required: true, + }, + ... + +func (d *httpDataSourceType) NewDataSource(context.Context, provider.Provider) (datasource.DataSource, diag.Diagnostics) { + return &httpDataSource{}, nil +} +``` + +This code from the `data_source_http.go` file defines the `Read` function for the `http` data source with the Framework. +The body of the function is omitted from this example for clarity, denoted by (`...`). + +```go +func (d *httpDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + ... +``` diff --git a/website/docs/plugin/framework/migrating/index.mdx b/website/docs/plugin/framework/migrating/index.mdx new file mode 100644 index 000000000..83d32e682 --- /dev/null +++ b/website/docs/plugin/framework/migrating/index.mdx @@ -0,0 +1,50 @@ +--- +page_title: 'Plugin Development: Migrating from SDKv2 to the plugin Framework' +description: >- + Migrate your provider from SDKv2 to the plugin Framework. +--- + +# Overview + +This guide helps you migrate a Terraform provider from SDKv2 to the plugin Framework. We recommend migrating because the Framework has abstractions that make it easier to use, and it represents the future of Terraform plugin development. Refer to [Which SDK should I Use?](/plugin/which-sdk) for more details. + +This guide provides information and examples for most common use cases, but it does not discuss every nuance of migration. You can ask additional migration questions in the [HashiCorp Discuss forum](https://discuss.hashicorp.com/c/terraform-providers/31). To request additions or updates to this guide, submit issues or pull requests to the [`terraform-plugin-framework` repository](https://github.com/hashicorp/terraform-plugin-framework). + +In addition to this migration guide, we recommend referring to the main [Framework documentation](/plugin/framework) as you migrate your provider. + +## Requirements + +Before you migrate your provider to the Framework, ensure it meets the the following requirements: + +- Go 1.18+ +- Built on the latest version of SDKv2 +- The provider is for use with Terraform >= 0.12.0 +## Muxing +Consider muxing when you need to migrate a provider that contains many resources or data sources. Muxing lets you use two versions of the same provider concurrently, with each serving different resources or data sources. This lets you migrate individual resources or data sources to the Framework one at a time. + +Refer to the [Combining and Translating documentation](/plugin/mux) for details about muxing configuration. + +## Testing Migration + +As you complete the migration, we recommend that you follow Test Driven Development by writing tests to ensure that the migration does not affect provider behavior. Refer to [Testing Migration](/plugin/framework/migrating/testing/testing#testing-migration) for details and an example. + +## Migration steps + +Take the following steps when you migrate a provider from SDKv2 to the Framework: + +* Ensure all [tests](/plugin/framework/migrating/testing#testing-migration) pass. +* [Serve the provider](/plugin/framework/migrating/provider#serving-the-provider) via the Framework. +* Implement [muxing](/plugin/framework/migrating/provider#muxing) if you plan to migrate the provider over several point releases. +* Update the [provider definition](/plugin/framework/migrating/provider#provider-definition) to use the Framework. +* Update the [provider schema](/plugin/framework/migrating/provider#provider-schema). +* Update each of the provider's resources and data sources. +* Update related [tests](/plugin/framework/migrating/testing) to use the Framework, and ensure that the tests fail. +* Update the [resource](/plugin/framework/migrating/resources#schema) or [data source](/plugin/framework/migrating/data-sources) schema. +* Update the resource or data source [CRUD function](/plugin/framework/migrating/resources/crud) definitions. +* Update the resource or data source's [attribute schema](/plugin/framework/migrating/attributes/schema). +* Update the resource's [import function](/plugin/framework/migrating/resources/import), if your provider implements importing the resource. +* Implement [plan modification](/plugin/framework/migrating/resources/plan-modification) if needed. +* Implement [state upgraders](/plugin/framework/migrating/resources/plan-modification) if needed. +* Verify that related tests now pass. +* If you used muxing, remove the muxing configuration. + diff --git a/website/docs/plugin/framework/migrating/provider.mdx b/website/docs/plugin/framework/migrating/provider.mdx new file mode 100644 index 000000000..fb8814362 --- /dev/null +++ b/website/docs/plugin/framework/migrating/provider.mdx @@ -0,0 +1,378 @@ +--- +page_title: 'Provider: Migrating from SDKv2 to the Framework' +description: >- + Migrate a provider definition and schema from SDKv2 to the plugin Framework. +--- + +# Provider + +Providers are Terraform plugins that define resources and data sources for practitioners to use. You serve your +providers with a provider server so they can interact with Terraform. + +This page explains how to migrate a provider server, definition, and schema from SDKv2 to the plugin Framework. + +## Serving the Provider + +You must update your provider's `main.go` file to serve Framework providers. + +### SDKv2 + +In SDKv2, the provider packages `main` function serves the provider by calling `plugin.Serve`. + +The following code shows a basic implementation for serving an SDKv2 provider. + +```go +func main() { + plugin.Serve( + &plugin.ServeOpts{ + ProviderFunc: provider.New, + ProviderAddr: "registry.terraform.io//", + }, + ) +} +``` +### Framework + +In the Framework, you serve your provider by calling `providerserver.Serve` in your provider package's `main` function. +Refer to [Provider Servers](/plugin/framework/provider-servers) in the Framework documentation for details. + +The following code shows an equivalent implementation for serving a provider in the Framework. + +```go +func main() { + err := providerserver.Serve( + context.Background(), + provider.New, + providerserver.ServeOpts{ + Address: "registry.terraform.io//", + }, + ) + + if err != nil { + log.Fatal(err) + } +} +``` + +### Muxing + +Muxing lets you use two versions of the same provider concurrently, with each serving different resources or data +sources. Refer to the [Combining and Translating documentation](/plugin/mux) for full details about muxing +configuration. Use muxing when you want to release a version of your provider with only some of the resources and data +sources migrated to the Framework. You may want to do this if your provider manages a large number of resources and +data sources. + +The following example shows how to set up muxing for a provider that uses protocol version 5 to maintain compatibility +with Terraform >= 0.12.0. The example also shows how to use the `debug` flag to optionally run the provider in debug +mode. + +```go +func main() { + ctx := context.Background() + + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + providers := []func() tfprotov5.ProviderServer{ + providerserver.NewProtocol5(provider.New()), + tftime.Provider().GRPCProvider, + } + + muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...) + if err != nil { + log.Fatal(err) + } + + var serveOpts []tf5server.ServeOpt + + if debug { + serveOpts = append(serveOpts, tf5server.WithManagedDebug()) + } + + err = tf5server.Serve( + "registry.terraform.io/hashicorp/time", + muxServer.ProviderServer, + serveOpts..., + ) + if err != nil { + log.Fatal(err) + } +} +``` + +## Provider Definition + +Providers built with SDKv2 use a `schema.Provider` struct to define their behavior, while Framework providers use a +type that implements the `provider.Provider` interface, which you must define. + +### SDKv2 + +The [`ProviderFunc`](/plugin/framework/migrating/provider#serving-the-provider) field on +`plugin.ServeOpts` requires a pointer to `schema.Provider`. This is typically satisfied by calling a function that +returns a pointer to `schema.Provider`. + +The `ResourcesMap` and `DataSourcesMap` fields each contain a map of strings to functions that each return a pointer +to a `schema.Resource` struct. + +The following example shows a basic implementation of an SDKv2 provider. For clarity, the fields for +`ProviderMetaSchema` and `TerraformVersion` are omitted from the `schema.Provider` (denoted by `...`). + +```go +func New() *schema.Provider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{}, + ConfigureContextFunc: configureContextFunc(), + ResourcesMap: map[string]*schema.Resource{ + "resource_example": resourceExample(), + }, + DataSourcesMap: map[string]*schema.Resource{ + "dataSource_example": dataSourceExample(), + }, + ... + } +} +``` + +### Framework + +In the Framework, the second argument to your `provider.Serve` function requires a function that returns a type +satisfying the `provider.Provider` interface. Refer to [Providers](/plugin/framework/providers) in the Framework +documentation for details. + +The following ocde shows a typical implementation. In this implementation, the `GetResources` function returns a map +from resource names (strings) to types that implement the `provider.ResourceType` interface. The `GetDataSources` +function returns a map from data source names (strings) to types that implement the `provider.DataSourceType` interface. +Refer to the [Resources](/plugin/framework/migrating/resources) and +[Data Sources](/plugin/framework/migrating/data-sources) pages in this guide to implement these functions for your +provider. + +```go +type provider struct { +} + +func New() provider.Provider { + return &provider{} +} + +func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{}, nil +} + +func (p *provider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { +} + +func (p *provider) GetResources(ctx context.Context) (map[string]provider.ResourceType, diag.Diagnostics) { + return map[string]provider.ResourceType{ + "resource_example": resourceTypeExample{}, + }, nil +} + +func (p *provider) GetDataSources(ctx context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { + return map[string]provider.DataSourceType{ + "data_source_example": dataSourceTypeExample{}, +}, nil +} +``` + +### Migration Notes +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- In SDKv2, your provider's `New` function returns a `schema.Provider` struct. In the Framework, `New` returns a type +that you define which satisfies the `provider.Provider` interface. +- In SDKv2, `Schema` is a field on `schema.Provider` that contains `map[string]*schema.Schema`, which maps attribute +names to `schema.Schema` structs. In the Framework, `GetSchema` is a function you define on your provider's +`provider.Provider` interface that returns your provider's `tfsdk.Schema` struct. +- In SDKv2, `ConfigureContextFunc` is a field on `schema.Provider` containing a function that configures the provider. +In the Framework, `Configure` is a function you define on your provider that configures your provider. +- In SDKv2, `ResourcesMap` is a field on `schema.Provider` containing `map[string]*schema.Resource`, which maps resource +names to `schema.Resource` structs. In the Framework, `GetResources` is a function you define on your provider that +returns `map[string]provider.ResourceType`, which maps resource names to types that you define, which satisfy the +`provider.ResourceType` interface. +- In SDKv2, `DataSourcesMap` is a field on `schema.Provider` containing `map[string]*schema.Resource`, which maps data +source names to `schema.Resource` structs (Data sources and resources both use `schema.Resource`). In the Framework, +`GetDataSources` is a function you define on your provider that returns `map[string]provider.DataSourceType`, which +maps data source names to types that you define, which satisfy the `provider.DataSourceType` interface. + +### Example + +The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) +provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-tls` repository and compare `provider.go` in +[v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/provider.go) +with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/provider.go). + +**SDKv2** + +The following example shows how to set up a provider schema, configuration, resources, and data sources using SDKv2. + +```go +func New() (*schema.Provider, error) { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "proxy": { + ... + }, + }, + ConfigureContextFunc: configureProvider, + ResourcesMap: map[string]*schema.Resource{ + "tls_private_key": resourcePrivateKey(), + ... + }, + DataSourcesMap: map[string]*schema.Resource{ + "tls_public_key": dataSourcePublicKey(), + ... + }, + }, nil +} +``` + +**Framework** + +The following shows the same section of provider code after the migration. + +```go +var _ provider.Provider = (*provider)(nil) + +func New() provider.Provider { + return &provider{} +} + +func (p *provider) GetResources(_ context.Context) (map[string]provider.ResourceType, diag.Diagnostics) { + return map[string]provider.ResourceType{ + "tls_private_key": &privateKeyResourceType{}, + ... + }, nil +} + +func (p *provider) GetDataSources(_ context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { + return map[string]provider.DataSourceType{ + "tls_public_key": &publicKeyDataSourceType{}, + ... + }, nil +} + +func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "proxy": { + ... + }, + }, + }, nil +} + +func (p *provider) Configure(ctx context.Context, req provider.ConfigureRequest, res *provider.ConfigureResponse) { + ... +} +``` + +## Provider Schema + +A provider schema defines the attributes and behaviors of the provider itself. For example, a provider that connects to +a third-party API may define attributes for the base URL or a required authentication token. + +### SDKv2 + +In SDKv2, you implement a provider Schema by populating the `Schema` field on the `schema.Provider` struct. The `Schema` +field contains a `map[string]*schema.Schema`. Each map entry represents the name of the attribute and pointer to a +`schema.Schema` struct that defines that attribute's behavior. + +The following example defines the provider schema in the `Schema` field within the `schema.Provider` struct. The rest +of the `schema.Provider` struct has been omitted for clarity (denoted by `...`). + +```go +func New() *schema.Provider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{}, + ... +``` +### Framework + +In the Framework, the `GetSchema` function returns the provider schema. The `GetSchema` function is part of the +`provider.Provider` interface that your provider must implement. `GetSchema` returns a struct containing fields for +`Attributes` and `Blocks`. These `Attributes and `Blocks` contain `map[string]tfsdk.Attribute` and +`map[string]tfsdk.Block`, respectively. Refer to [Providers - GetSchema](/plugin/framework/providers#getschema) in the +Framework documentation for details. + +The following code shows the `GetSchema` function, which returns the provider schema. + +```go +func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{}, nil +} +``` + +Refer to the [Attributes](/plugin/framework/migrating/attributes-blocks#attributes) and +[Blocks](/plugin/framework/migrating/attributes-blocks#blocks) pages in this migration guide to learn how to migrate +those fields to the Framework. + +### Migration Notes +Remember the following differences between SDKv2 and the Framework when completing the migration. + +In SDKv2, `schema.Schema` is a struct that defines attributes and behaviors (e.g., `Type`, `Optional`). In the +Framework `tfsdk.Schema` is a struct that defines `Attributes`, which are structs that define attributes and behaviors +(e.g., `Type`, `Optional`). + +### Example + +The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) +provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-tls` repository and compare `provider.go` in +[v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/provider.go) +with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/provider.go). + +This example also shows how to use a nested block and a nested attribute for the SDKv2 and Framework examples, +respectively. Refer to the +[Blocks and Nested Attributes](/plugin/framework/migrating/attributes-blocks) page in this guide for more details. + +**SDKv2** + +The following example shows how to configure an attribute within a provider schema using SDKv2. + +```go +Schema: map[string]*schema.Schema{ + "proxy": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validation.ToDiagFunc(validation.IsURLWithScheme(SupportedProxySchemesStr())), + ConflictsWith: []string{"proxy.0.from_env"}, + Description: "URL used to connect to the Proxy. " + + fmt.Sprintf("Accepted schemes are: `%s`. ", strings.Join(SupportedProxySchemesStr(), "`, `")), + }, + ... +``` + +**Framework** + +The following example shows how to configure provider schema using the Framework. + +```go +func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "proxy": { + Optional: true, + Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "url": { + Type: types.StringType, + Optional: true, + Validators: []tfsdk.AttributeValidator{ + attribute_validator.UrlWithScheme(supportedProxySchemesStr()...), + schemavalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), + }, + MarkdownDescription: "URL used to connect to the Proxy. " + + fmt.Sprintf("Accepted schemes are: `%s`. ", strings.Join(supportedProxySchemesStr(), "`, `")), + }, +``` diff --git a/website/docs/plugin/framework/migrating/resources/crud.mdx b/website/docs/plugin/framework/migrating/resources/crud.mdx new file mode 100644 index 000000000..cd2f81803 --- /dev/null +++ b/website/docs/plugin/framework/migrating/resources/crud.mdx @@ -0,0 +1,192 @@ +--- +page_title: 'Resources - CRUD Functions: Migrating from SDKv2 to the Framework' +description: >- + Migrate resource create, read, update, and delete (CRUD) functions from SDKv2 to the plugin Framework. +--- + +# CRUD functions + +In Terraform, a resource represents a single instance of a given resource type. They modify a specific resource in the +API and in Terraform's state through a set of Create, Read, Update, and Delete (CRUD) functions. A resource's CRUD +functions implement the logic required to manage your resources with Terraform. Refer to +[Resources - Define Resources](/plugin/framework/resources#define-resources) in the Framework documentation for details. + +This page explains how to migrate a resource's CRUD functions from SDKv2 to the plugin Framework. + +## SDKv2 + +In SDKv2, a resource's CRUD functions are defined by populating the relevant fields (e.g., `CreateContext`, +`ReadContext`) on the `schema.Resource` struct. + +The following code shows a basic implementation of CRUD functions with SDKv2. For clarity, the rest of the +`schema.Resource` struct is omitted (denoted by `...`). + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + CreateContext: create, + ReadContext: read, + UpdateContext: update, + DeleteContext: delete, + ... +``` + +## Framework +In the Framework, you implement CRUD functions for your resource by defining a type that implements the +`resource.Resource` interface. This type is returned by the `NewResource` function on the type implementing the +`provider.ResourceType` interface. To define functions related to state upgrade, import, and plan modification, +implement their respective interfaces on your resource: `ResourceWithUpgradeState`, `ResourceWithImportState`, and +`ResourceWithModifyPlan`. + +The following code shows how you define a `resource.Resource` which implements CRUD functions with the Framework. + +```go +type resourceExample struct { + p provider +} + +func (r *resourceExample) Create(ctx context.Context, req resource.CreateRequest, resp +*resource.CreateResponse) { + ... +} + +func (r *resourceExample) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + ... +} + +func (r *resourceExample) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + ... +} + +func (r *resourceExample) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + ... +} +``` +## Migration Notes +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- In SDKv2 it is not necessary to define functions for parts of the CRUD lifecycle that are not used by a given +resource. For instance, if the resource does not support in-place modification, you do not need to define an `Update` +function. In the Framework, you must implement each of the CRUD lifecycle functions on all resources to satisfy the +`Resource` interface, even if the function does nothing. +- In the Framework, there is no requirement to explicitly call `resp.State.RemoveResource()` in the `Delete` function, +as this is handled automatically by the Framework itself. +- In SDKv2, you get and set attribute values in Terraform's state by calling `Get()` and `Set()` on +`schema.ResourceData`. In the Framework, you get attribute values from the configuration and plan by accessing +`Config` and `Plan` on `resource.CreateRequest`. You set attribute values in Terraform's state by mutating `State` +on `resource.CreateResponse`. + +## Example + +The following examples show how to migrate portions of the +[random](https://github.com/hashicorp/terraform-provider-random) provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-random` repository and compare the `resource_password.go` file in +[v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) +with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). + +**SDKv2** + +The following example from the `resource_password.go` file shows implementations of CRUD functions on the +`random_password` resource with SDKv2. The `UpdateContext` function is not implemented because the provider does not +support updating this resource. + +```go +func resourcePassword() *schema.Resource { + return &schema.Resource{ + CreateContext: createPassword, + ReadContext: readNil, + DeleteContext: RemoveResourceFromState, + ... +``` + +The following example shows the implementation of the `createPassword()` function with SDKv2. The implementations of +the `readNil()` and `RemoveResourceFromState()` functions are not shown for brevity. + +```go +func createPassword(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + diags := createStringFunc(true)(ctx, d, meta) + if diags.HasError() { + return diags + } + + hash, err := generateHash(d.Get("result").(string)) + if err != nil { + diags = append(diags, diag.Errorf("err: %s", err)...) + return diags + } + + if err := d.Set("bcrypt_hash", hash); err != nil { + diags = append(diags, diag.Errorf("err: %s", err)...) + return diags + } + + return nil +} +``` + +**Framework** +The following shows the same section of provider code after the migration. + +This code implements the `Create()` function for the `random_password` resource with the Framework. + +```go +func (r *passwordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan passwordModelV2 + + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + params := random.StringParams{ + Length: plan.Length.Value, + Upper: plan.Upper.Value, + MinUpper: plan.MinUpper.Value, + Lower: plan.Lower.Value, + MinLower: plan.MinLower.Value, + Numeric: plan.Numeric.Value, + MinNumeric: plan.MinNumeric.Value, + Special: plan.Special.Value, + MinSpecial: plan.MinSpecial.Value, + OverrideSpecial: plan.OverrideSpecial.Value, + } + + result, err := random.CreateString(params) + if err != nil { + resp.Diagnostics.Append(diagnostics.RandomReadError(err.Error())...) + return + } + + state := passwordModelV2{ + ID: types.String{Value: "none"}, + Keepers: plan.Keepers, + Length: types.Int64{Value: plan.Length.Value}, + Special: types.Bool{Value: plan.Special.Value}, + Upper: types.Bool{Value: plan.Upper.Value}, + Lower: types.Bool{Value: plan.Lower.Value}, + Numeric: types.Bool{Value: plan.Numeric.Value}, + MinNumeric: types.Int64{Value: plan.MinNumeric.Value}, + MinUpper: types.Int64{Value: plan.MinUpper.Value}, + MinLower: types.Int64{Value: plan.MinLower.Value}, + MinSpecial: types.Int64{Value: plan.MinSpecial.Value}, + OverrideSpecial: types.String{Value: plan.OverrideSpecial.Value}, + Result: types.String{Value: string(result)}, + } + + hash, err := generateHash(plan.Result.Value) + if err != nil { + resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) + } + + state.BcryptHash = types.String{Value: hash} + + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} +``` diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx new file mode 100644 index 000000000..8560e3bb8 --- /dev/null +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -0,0 +1,173 @@ +--- +page_title: 'Resources - Import: Migrating from SDKv2 to the Framework' +description: >- + Practitioners use the import command to let Terraform manage existing infrastructure resources. + Migrate import functions from SDKv2 to the plugin Framework. +--- + +# Import + +Practitioners can use the [`terraform import` command](https://www.terraform.io/cli/commands/import) to let Terraform +begin managing existing infrastructure by importing an existing resource into their Terraform project's state. A +resource's importer function implements the logic to add a resource to Terraform's state. Refer to +[Resources - Import](/plugin/framework/resources/import) in the Framework documentation for details. + +This page explains how to migrate import functions from SDKv2 to the plugin Framework. + +## SDKv2 + +In SDKv2, the `Importer` field on the `schema.Resource` defines how the provider imports resources to Terraform's +state. The following example implements resource import with SDKv2. The rest of the `schema.Resource` struct is omitted +for clarity (denoted by `...`). + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + Importer: &schema.ResourceImporter{ + StateContext: StateContextFunc, + }, + ... +``` + +The `StateContextFunc` is the function called to import a resource into Terraform state. Any operations requiredthat +are needed to import the resource take place within this function and may result in the mutation of the `ResourceData` +that is passed into the function. The return value is a slice of `schema.ResourceData`. This slice, which may be simple, likemight be as simple as returning the `ResourceData` that was passed into the function, or it may involve multipleinvolve fan out to multiple resources, such as AWS security groups. + +## Framework + +In the Framework, you implement the `ResourceWithImportState` interface on your `resource.Resource` type to allow +users to import a given resource. + +The following code shows how you define an `ImportState` function with the Framework. The body of the `ImportState` +function is omitted for clarity (denoted by `...`). + +```go +func (r *resourceExample) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + ... +} +``` + +The `ImportState` function includes a `resource.ImportStateResponse`, which you use to mutate and set your resource's +state by mutating the state. + +## Migration Notes + +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- In both SDKv2 and Framework, there is no access to the configuration, state, or plan during import. The import +functions can only access the value (e.g., `ID`) supplied to the `terraform import` command. +- In SDKv2, you implement resource importing by populating the `Importer` field on the `schema.Resource` struct. In the +Framework, you define an `ImportState` function on the type which implements `resource.Resource`. This implementation +satisfies the `tfsdk.ResourceWithImportState` interface. + +## Example + +The following examples show how to migrate portions of the +[random](https://github.com/hashicorp/terraform-provider-random) provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-random` repository and compare the `resource_password.go` file in +[v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) +with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). + +This example also shows one way to handle populating attributes with their default values during import. + +**SDKv2** + +The following example from the `resource_password.go` file shows the import function for the `random_password` resource +with SDKv2. + +```go +func resourcePassword() *schema.Resource { + return &schema.Resource{ + Importer: &schema.ResourceImporter{ + StateContext: importPasswordFunc, + }, + ... + } +} +``` + +The following example shows the implementation of the `importPasswordFunc` function with SDKv2. + +```go +func importPasswordFunc(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + for k, v := range passwordSchemaV2() { + if v.Default == nil { + continue + } + if err := d.Set(k, v.Default); err != nil { + return nil, fmt.Errorf("error setting %s: %w", k, err) + } + } + + for _, key := range []string{"number", "numeric"} { + if err := d.Set(key, true); err != nil { + return nil, fmt.Errorf("error setting %s: %w", key, err) + } + } + + val := d.Id() + d.SetId("none") + + if err := d.Set("result", val); err != nil { + return nil, fmt.Errorf("resource password import failed, error setting result: %w", err) + } + + if err := d.Set("length", len(val)); err != nil { + return nil, fmt.Errorf("error setting length: %w", err) + } + + hash, err := generateHash(val) + if err != nil { + return nil, fmt.Errorf("resource password import failed, generate hash error: %w", err) + } + + if err := d.Set("bcrypt_hash", hash); err != nil { + return nil, fmt.Errorf("resource password import failed, error setting bcrypt_hash: %w", err) + } + + return []*schema.ResourceData{d}, nil +} +``` + +**Framework** +The following shows the same section of provider code after the migration. + +This code implements the `ResourceWithImportState` interface on the `passwordResource` type by defining an`ImportState` +function. + +```go +func (r *passwordResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + id := req.ID + + state := passwordModelV2{ + ID: types.String{Value: "none"}, + Result: types.String{Value: id}, + Length: types.Int64{Value: int64(len(id))}, + Special: types.Bool{Value: true}, + Upper: types.Bool{Value: true}, + Lower: types.Bool{Value: true}, + Numeric: types.Bool{Value: true}, + MinSpecial: types.Int64{Value: 0}, + MinUpper: types.Int64{Value: 0}, + MinLower: types.Int64{Value: 0}, + MinNumeric: types.Int64{Value: 0}, + } + + state.Keepers.ElemType = types.StringType + + hash, err := generateHash(id) + if err != nil { + resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) + } + + state.BcryptHash = types.String{Value: hash} + + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} +``` diff --git a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx new file mode 100644 index 000000000..4aafebe71 --- /dev/null +++ b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx @@ -0,0 +1,191 @@ +--- +page_title: 'Resources - CustomizeDiff and PlanModifiers: Migrating from SDKv2 to the Framework' +description: >- + Migrate resource customizediff functions in SDKv2 to plan modifiers in the plugin Framework. +--- + +# Plan Modification +Your provider can modify the Terraform plan to match the expected end state. This can include replacing unknown values +with expected known values or marking a resource that must be replaced. Refer to +[Plan Modification](/plugin/framework/resources/plan-modification) in the Framework documentation for details. + +This page explains how to migrate resource `CustomizeDiff` functions in SDKv2 to `PlanModifiers` in the plugin +Framework. + +## SDKv2 +In SDKv2, plan modification is implemented with the `CustomizeDiff` field on the `schema.Resource` struct. The following +code shows a basic implementation of plan modification with SDKv2. The rest of the `schema.Resource` struct is omitted +for clarity (denoted by `...`). + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + CustomizeDiff: CustomizeDiffFunc, + ... +``` + +## Framework + +In the Framework, you implement plan modification either by implementing the `ResourceWithModifyPlan` interface on your +resource type, or by implementing `PlanModifiers` on individual attributes. This page demonstrates how to implement the +`Plan Modifiers` on individual attributes. Refer to +[Attributes - Default Values](/plugin/framework/migrating//attributes#default-values) and +[Attributes - Force New](/plugin/framework/migrating//attributes#force-new) in this guide for further information on how +to implement a plan modifier on an attribute. + +The `ResourceWithModifyPlan` interface requires a `ModifyPlan` function. + +The following code shows how you can implement the `ModifyPlan` function on your `resource.Resource` type. The rest of +the `ModifyPlan` function is omitted for clarity (denoted by `...`). + +```go +func (r *resourceExample) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + ... +} +``` +## Migration Notes +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- In SDKv2, you implement plan modification with the `CustomizeDiff` field on the `schema.Resource` struct. In the +Framework, you can either implement plan modification for the entire resource by implementing the +`ResourceWithModifyPlan` interface, or on individual attributes by adding `PlanModifiers` to your resource attributes. +- Many existing CustomizeDiff implementations may be better suited to implementation as attribute plan modifiers in the +Framework. + +## Example + +The following examples show how to migrate portions of the +[random](https://github.com/hashicorp/terraform-provider-random) provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-random` repository and compare the `resource_password.go` file in +[v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) +with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). + +**SDKv2** + +In SDKv2, the `CustomizeDiff` field on the `schema.Resource` struct refers to a function or set of functions that +implement plan modification. + +The following example from the `resource_password.go` files shows the use of `CustomizeDiff` to keep two attributes +synchronized (i.e., ensure that they contain the same value) with SDKv2. + +```go +func resourcePassword() *schema.Resource { + ... + customizeDiffFuncs = append(customizeDiffFuncs, planSyncIfChange("number", "numeric")) + customizeDiffFuncs = append(customizeDiffFuncs, planSyncIfChange("numeric", "number")) + + return &schema.Resource{ + ... + CustomizeDiff: customdiff.All( + customizeDiffFuncs..., + ), + } +} +``` + +The following example shows the implementation of the `planSyncIfChange` function. + +```go +func planSyncIfChange(key, keyToSync string) func(context.Context, *schema.ResourceDiff, interface{}) error { + return customdiff.IfValueChange( + key, + func(ctx context.Context, oldValue, newValue, meta interface{}) bool { + return oldValue != newValue + }, + func(_ context.Context, d *schema.ResourceDiff, _ interface{}) error { + return d.SetNew(keyToSync, d.Get(key)) + }, + ) +} +``` + +**Framework** + +Many existing `CustomizeDiff` implementations would be better suited to migration to attribute plan modifiers in the +Framework. This code shows the implementation using attribute plan modifiers with the Framework. + +```go +func passwordSchemaV2() tfsdk.Schema { + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + ... + "number": { + ... + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.NumberNumericAttributePlanModifier(), + ... + }, + }, + + "numeric": { + ... + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.NumberNumericAttributePlanModifier(), + ... + }, + }, +``` + +The following shows an implementation of `NumberNumericAttributePlanModifier` in the Framework. + +```go +func NumberNumericAttributePlanModifier() tfsdk.AttributePlanModifier { + return &numberNumericAttributePlanModifier{} +} + +type numberNumericAttributePlanModifier struct { +} + +func (d *numberNumericAttributePlanModifier) Description(ctx context.Context) string { + return "Ensures that number and numeric attributes are kept synchronised." +} + +func (d *numberNumericAttributePlanModifier) MarkdownDescription(ctx context.Context) string { + return d.Description(ctx) +} + +func (d *numberNumericAttributePlanModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) { + numberConfig := types.Bool{} + diags := req.Config.GetAttribute(ctx, path.Root("number"), &numberConfig) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + numericConfig := types.Bool{} + req.Config.GetAttribute(ctx, path.Root("numeric"), &numericConfig) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + if !numberConfig.Null && !numericConfig.Null { + resp.Diagnostics.AddError( + "Number numeric attribute plan modifier failed", + "Cannot specify both number and numeric in config", + ) + return + } + + // Default to true for both number and numeric when both are null. + if numberConfig.Null && numericConfig.Null { + resp.AttributePlan = types.Bool{Value: true} + return + } + + // Default to using value for numeric if number is null + if numberConfig.Null && !numericConfig.Null { + resp.AttributePlan = numericConfig + return + } + + // Default to using value for number if numeric is null + if !numberConfig.Null && numericConfig.Null { + resp.AttributePlan = numberConfig + return + } +} +``` diff --git a/website/docs/plugin/framework/migrating/resources/schema.mdx b/website/docs/plugin/framework/migrating/resources/schema.mdx new file mode 100644 index 000000000..ec353427a --- /dev/null +++ b/website/docs/plugin/framework/migrating/resources/schema.mdx @@ -0,0 +1,224 @@ +--- +page_title: 'Resources - Schema: Migrating from SDKv2 to the Framework' +description: >- + Schemas define resource fields and give Terraform metadata about those fields. + Migrate a resource schema from SDKv2 to the plugin Framework. +--- + +# Resource Schema + +Resources are an abstraction that allow Terraform to manage infrastructure objects by defining create, read, update, +and delete functionality that maps onto API operations. Resource schemas define what fields a resource has, give +Terraform metadata about those fields, and define how the resource behaves. Refer to +[Resources](/plugin/framework/resources) in the Framework documentation for details. + +This page explains how to migrate a resource's schema from SDKv2 to the plugin Framework. + +## SDKv2 +In SDKv2, resources are defined by the `ResourcesMap` field in the `schema.Provider` struct, which maps resource names +(strings) to their schema. Each schema is a `schema.Resource` struct that includes: + +- A `Schema` field, which defines resource attributes +- Fields for resource lifecycle functions such as `Create` and `CreateContext` +- Fields for functions to implement state upgrade (`StateUpgraders`), import (`Importer`), and customize diff +(`CustomizeDiff`) + +The following code shows a basic implementation of resource schema with SDKv2. For clarity, the rest of the +`schema.Provider` struct is omitted (denoted by `...`). + +```go +func New() *schema.Provider { + return &schema.Provider{ + ResourcesMap: map[string]*schema.Resource + }{ + "resource_example": resourceExample(), + } + ... +}, +``` + +SDKv2 defines the `schema.Resource` struct as follows. + +```go +schema.Resource{ + Schema map[string]*Schema + SchemaVersion int + MigrateState StateMigrateFunc + StateUpgraders []StateUpgrader + Create CreateFunc + Read ReadFunc + Update UpdateFunc + Delete DeleteFunc + Exists ExistsFunc + CreateContext CreateContextFunc + ReadContext ReadContextFunc + UpdateContext UpdateContextFunc + DeleteContext DeleteContextFunc + CreateWithoutTimeout CreateContextFunc + ReadWithoutTimeout ReadContextFunc + UpdateWithoutTimeout UpdateContextFunc + DeleteWithoutTimeout DeleteContextFunc + CustomizeDiff CustomizeDiffFunc + Importer *ResourceImporter + DeprecationMessage string + Timeouts *ResourceTimeout + Description string + UseJSONNumber bool +} +``` + +## Framework + +In the Framework, you define your provider's resources by adding them to your provider's `GetResources` function. + +The `GetResources` function on your `provider.Provider` returns a map from the resource name (string) to a struct that +implements the `ResourceType` interface for each resource your provider supports. + +The following code shows how you add a resource to your provider with the Framework. + +```go +func (p *provider) GetResources(ctx context.Context) (map[string]provider.ResourceType, diag.Diagnostics) { + return map[string]provider.ResourceType{ + "resource_example": resourceTypeExample{}, + }, nil +} +``` + +The `provider.ResourceType` interface requires `GetSchema` and `NewResource` functions. + +The `GetSchema` function returns a `tfsdk.Schema` struct which defines your resource's attributes. This is the same +struct you use to define provider and data source attributes. + +The `NewResource` function returns a type that you define. The type implements the `resource.Resource` interface, +including the CRUD functions for your resource. + +The following code shows how you define a `provider.ResourceType` which implements these two functions with the +Framework. + +```go +type resourceTypeExample struct{} + +func (r resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{}, nil +} + +func (r resourceTypeExample) NewResource(_ context.Context, p provider.Provider) (resource.Resource, diag.Diagnostics) { + return &resourceExample{}, nil +} +``` + +The `tfsdk.Schema` is used for defining the schema for providers, resources and data sources. The Framework defines the +`tfsdk.Schema` struct as follows. + +```go +type Schema struct { + Attributes map[string]Attribute + Blocks map[string]Block + Version int64 + DeprecationMessage string + Description string + MarkdownDescription string +} +``` + +Refer to the [Resources - CRUD functions](FIXME: link) page in this guide to learn how to define the `resource.Resource` +type returned by your resource type's `NewResource` function. + +## Migration Notes + +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- SDKv2 uses `schema.Resource` structs to define resources. These structs have a `Schema` field which holds a +`schema.Schema` to define the resource's attributes and behavior. In the Framework, you define a type that implements +the `ResourceType` interface, which includes `GetSchema` that returns your resource's schema. +- SDKv2 implements a resource's CRUD operations as functions on the `schema.Resource`. In the Framework, you define a +type that implements the `Resource` interface. The resource interface contains the functions that define your resource's +CRUD operations. + +## Example + +The following examples show how to migrate portions of the +[tls](https://github.com/hashicorp/terraform-provider-tls) provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, +clone the `terraform-provider-tls` repository and compare the `resource_private_key.go` file in +[v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/resource_private_key.go) with +[v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/resource_private_key.go). + +**SDKv2** + +In SDKv2, the `ResourcesMap` field on the `schema.Provider` struct holds a `map[string]*schemaResource`. A typical +pattern is to implement a function that returns `schema.Resource`. + +The following example from the `provider.go` file defines a `tls_private_key` resource within the provider schema. + +```go +func New() (*schema.Provider, error) { + return &schema.Provider { + ResourcesMap: map[string]*schema.Resource { + "tls_private_key": resourcePrivateKey(), + ... +``` + +The following example from the `resource_private_key.go` file defines the resource schema. + +```go +func resourcePrivateKey() *schema.Resource { + return &schema.Resource{ + CreateContext: createResourcePrivateKey, + DeleteContext: deleteResourcePrivateKey, + ReadContext: readResourcePrivateKey, + + Description: "Creates a PEM ...", + + Schema: map[string]*schema.Schema{ + "algorithm": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(SupportedAlgorithmsStr(), false)), + Description: "Name of the algorithm to use when generating the private key. " + + "Currently-supported values are `RSA`, `ECDSA` and `ED25519`.", + }, + ... +``` + +**Framework** + +The following shows the same section of provider code after the migration. + +This code defines the `tls_private_key` resource by mapping the resource name to the `privateKeyResourceType` struct. + +```go +func (p *provider) GetResources(_ context.Context) (map[string]provider.ResourceType, diag.Diagnostics) { + return map[string]provider.ResourceType{ + "tls_private_key": &privateKeyResourceType{}, + ... +``` + +This code defines the `GetSchema` and `NewResource` functions for the `privateKeyResourceType`. + +```go +func (rt *privateKeyResourceType) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Version: 1, + Attributes: map[string]tfsdk.Attribute{ + // Required attributes + "algorithm": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + Validators: []tfsdk.AttributeValidator{ + attribute_validation.OneOf(supportedAlgorithmsAttrValue()...), + }, + Description: "Name of the algorithm to use when generating the private key. " + + fmt.Sprintf("Currently-supported values are: `%s`. ", strings.Join(supportedAlgorithmsStr(), "`, `")), + }, + ... + +func (rt *privateKeyResourceType) NewResource(_ context.Context, _ provider.Provider) (resource.Resource, diag.Diagnostics) { + return &privateKeyResource{}, nil +} +``` diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx new file mode 100644 index 000000000..2b7e867da --- /dev/null +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -0,0 +1,207 @@ +--- +page_title: 'Resources - State Upgrading: Migrating from SDKv2 to the Framework' +description: >- + State upgraders let users update resources provisioned with old schema configurations. + Migrate resource StateUpgraders in SDKv2 to UpgradeState in the plugin Framework. +--- + +# State Upgraders + +When you update the resource implementation in your provider, some changes may not be compatible with old versions. You +can create state upgraders to help users migrate resources provisioned with old schema configurations. Refer to +[State Upgrade](/plugin/framework/resources/state-upgrade) in the Framework documentation for details. + +This page explains how to migrate resource `StateUpgraders` in SDKv2 to `UpgradeState` in the plugin Framework. + +## SDKv2 + +In SDKv2, state upgraders are defined by populating the `StateUpgraders` field on the `schema.Resource` struct. Refer +to [State Migration](/plugin/sdkv2/resources/state-migration) in the SDKv2 documentation for details. + +The following code shows a basic implementation of the `stateUpgraders` field in SDKv2. For clarity, the rest of the +`schema.Resource` struct is omitted (denoted by `...`). + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + StateUpgraders: []schema.StateUpgrader{ + { + Version: int, + Type: cty.Type, + Upgrade: StateUpgradeFunc, + }, + ... +``` + +## Framework + +In the Framework, you implement the `ResourceWithUpgradeState` on your `resource.Resource` type to upgrade your +resource's state when required. + +The following code shows how you define an `UpgradeState` function with the Framework. The body of the `UpgradeState` +function is omitted for clarity (denoted by `...`). + +```go +func (r *resourceExample) UpgradeState(context.Context) map[int64]resource.StateUpgrader { + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: *tfsdk.Schema, + StateUpgrader: func(context.Context, UpgradeStateRequest, *UpgradeStateResponse), + }, + ... +``` + +The `UpgradeState` function returns a map from state versions to structs that implement state upgrade from the given +version to the latest version. + +## Migration Notes + +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- In SDKv2, you implement state upgraders populating the `StateUpgraders` field on the `schema.Resource` struct. In the +Framework, you define an `UpgradeState` function on the resource itself. +- In SDKv2, state upgraders apply each state upgrader in turn. For example, version 0 => version 1, version 1 => +version 2. In the Framework, each `UpgradeState` function is required to perform all of the necessary transformations in +a single step. For example, version 0 => version 2, version 1 => version 2. + +## Example + +The following examples show how to migrate portions of the +[random](https://github.com/hashicorp/terraform-provider-random) provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +`terraform-provider-random` repository and compare the `resource_password.go` file in +[v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) +with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). + +**SDKv2** + +In SDKv2 the `schema.Resource` struct has a `StateUpgraders` field that holds `[]schema.StateUpgrader` struct(s). + +The following example from the `resource_password.go` file shows the state upgrade functions for the `random_password` +resource with SDKv2. + +```go +func resourcePassword() *schema.Resource { + return &schema.Resource{ + Schema: passwordSchemaV2(), + SchemaVersion: 2, + StateUpgraders: []schema.StateUpgrader{ + { + Version: 0, + Type: resourcePasswordV0().CoreConfigSchema().ImpliedType(), + Upgrade: resourcePasswordStateUpgradeV0, + }, + { + Version: 1, + Type: resourcePasswordV1().CoreConfigSchema().ImpliedType(), + Upgrade: resourcePasswordStringStateUpgradeV1, + }, + }, + ... +``` + +The following example shows the implementation of the `resourcePasswordStateUpgradeV0` function with SDKv2. + +```go +func resourcePasswordStateUpgradeV0(_ context.Context, rawState map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + if rawState == nil { + return nil, fmt.Errorf("resource password state upgrade failed, state is nil") + } + + result, ok := rawState["result"].(string) + if !ok { + return nil, fmt.Errorf("resource password state upgrade failed, result is not a string: %T", rawState["result"]) + } + + hash, err := generateHash(result) + if err != nil { + return nil, fmt.Errorf("resource password state upgrade failed, generate hash error: %w", err) + } + + rawState["bcrypt_hash"] = hash + + return rawState, nil +} +``` + +**Framework** + +The following shows the same section of provider code after the migration. + +This code implements the `ResourceWithUpgradeState` interface on the `passwordResource` type by defining an +`UpgradeState` function. The `UpgradeState` function returns a map from each state version (int64) to a +`ResourceStateUpgrader` struct. + +```go +func (r *passwordResource) UpgradeState(context.Context) map[int64]resource.StateUpgrader { + schemaV0 := passwordSchemaV0() + schemaV1 := passwordSchemaV1() + + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: &schemaV0, + StateUpgrader: upgradePasswordStateV0toV2, + }, + 1: { + PriorSchema: &schemaV1, + StateUpgrader: upgradePasswordStateV1toV2, + }, + } +} +``` + +This code implements the `upgradePasswordStateV0toV2` state upgrade function. + +```go +func upgradePasswordStateV0toV2(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + type modelV0 struct { + ID types.String `tfsdk:"id"` + Keepers types.Map `tfsdk:"keepers"` + Length types.Int64 `tfsdk:"length"` + Special types.Bool `tfsdk:"special"` + Upper types.Bool `tfsdk:"upper"` + Lower types.Bool `tfsdk:"lower"` + Number types.Bool `tfsdk:"number"` + MinNumeric types.Int64 `tfsdk:"min_numeric"` + MinUpper types.Int64 `tfsdk:"min_upper"` + MinLower types.Int64 `tfsdk:"min_lower"` + MinSpecial types.Int64 `tfsdk:"min_special"` + OverrideSpecial types.String `tfsdk:"override_special"` + Result types.String `tfsdk:"result"` + } + + var passwordDataV0 modelV0 + + resp.Diagnostics.Append(req.State.Get(ctx, &passwordDataV0)...) + if resp.Diagnostics.HasError() { + return + } + + passwordDataV2 := passwordModelV2{ + Keepers: passwordDataV0.Keepers, + Length: passwordDataV0.Length, + Special: passwordDataV0.Special, + Upper: passwordDataV0.Upper, + Lower: passwordDataV0.Lower, + Numeric: passwordDataV0.Number, + MinNumeric: passwordDataV0.MinNumeric, + MinLower: passwordDataV0.MinLower, + MinSpecial: passwordDataV0.MinSpecial, + OverrideSpecial: passwordDataV0.OverrideSpecial, + Result: passwordDataV0.Result, + ID: passwordDataV0.ID, + } + + hash, err := generateHash(passwordDataV2.Result.Value) + if err != nil { + resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) + return + } + + passwordDataV2.BcryptHash.Value = hash + + diags := resp.State.Set(ctx, passwordDataV2) + resp.Diagnostics.Append(diags...) +} +``` diff --git a/website/docs/plugin/framework/migrating/schema.mdx b/website/docs/plugin/framework/migrating/schema.mdx new file mode 100644 index 000000000..6aa6bedfb --- /dev/null +++ b/website/docs/plugin/framework/migrating/schema.mdx @@ -0,0 +1,142 @@ +--- +page_title: 'Provider: Migrating from SDKv2 to the Framework' +description: >- + Migrate a schema from SDKv2 to the plugin Framework. +--- + +# Schema + +Providers, resources, and data sources all use schema to define their attributes and behavior. Schemas specify the +constraints of Terraform configuration blocks and how the provider, resource, or data source behaves. + +This page explains the differences between the schema used by SDKv2 and the Framework. + +## Schema Structs + +SDKv2 uses `schema.Schema` structs to define the structure, type, and behavior of values drawn from configuration, +state, or plan data. The same `schema.Schema` struct type is used for providers, resources, and data sources. The +schema struct is returned by the function that creates the provider, resource, or data source in question. + +The Framework uses `tfsdk.Schema` structs for providers, resources, and data sources. The schema struct is returned by +a `GetSchema` function you define for the provider and each resource type and data source type. Refer to +[Framework](#framework) for details. + +## SDKv2 + +The following code shows basic implementations using `schema.Schema` structs to define schemas for providers, resources, +and data sources with SDKv2. + +```go +func New() *schema.Provider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{}, + ... + } +} +``` + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{}, + ... + } +} +``` + +```go +func dataSourceExample() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{}, + ... + } +} +``` + +SDKv2 defines the `schema.Schema` struct as follows. + +```go +type Schema struct { + Type ValueType + ConfigMode SchemaConfigMode + Required bool + Optional bool + Computed bool + ForceNew bool + DiffSuppressFunc SchemaDiffSuppressFunc + DiffSuppressOnRefresh bool + Default interface{} + DefaultFunc SchemaDefaultFunc + Description string + StateFunc SchemaStateFunc + Elem interface{} + MaxItems int + MinItems int + Set SchemaSetFunc + ConflictsWith []string + ExactlyOneOf []string + AtLeastOneOf []string + RequiredWith []string + Deprecated string + ValidateFunc SchemaValidateFunc + ValidateDiagFunc SchemaValidateDiagFunc + Sensitive bool +} +``` + +## Framework + +In the Framework, you implement `GetSchema` functions for your provider, resources, and data sources. This function is +required by the `provider.Provider`, `provider.ResourceType`, and `provider.DataSourceType` interfaces, respectively. +Refer to [Schemas](/plugin/framework/schemas) in the Framework documentation for details. + +The following code shows how you define the `GetSchema` function for your provider, resources, and data sources. + +```go +func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{}, nil +} +``` + +```go +func (r resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{}, nil +} +``` + +```go +func (r dataSourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{}, nil +} +``` + +The Framework defines the `schema.Schema` struct as follows. + +```go +type Schema struct { + Attributes map[string]Attribute + Blocks map[string]Block + Version int64 + DeprecationMessage string + Description string + MarkdownDescription string +} +``` + +You use the `Attributes` field to define attributes for your provider, resources, and data sources. You use the +`Blocks` to define named blocks. + +## Migration Notes +Remember the following differences between SDKv2 and the Framework when completing the migration. + +- SDKv2 uses `schema.Schema` structs to define the provider, resources, and data sources. The Framework uses +`tfsdk.Schema` structs instead. +- In SDKv2, schema structs are returned when a provider, resource, or data type is created. In the Framework, the +provider and each resource and data type have a `GetSchema` function that returns the schema. +- The `tfsdk.Schema` struct includes fields that you use to define +[attributes](/plugin/framework/migrating/attributes-and-blocks/attribute-schema) and +[blocks](/plugin/framework/migrating/attributes-and-blocks/blocks) for your provider and each resource +and data source. +- When you populate the `Version` field in `tfsdk.Schema` for a resource in the Framework, you copy the `Version` +field in `schema.Schema` in SDKv2. + diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx new file mode 100644 index 000000000..481b92781 --- /dev/null +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -0,0 +1,170 @@ +--- +page_title: 'Testing Migration: Migrating from SDKv2 to the Framework' +description: >- + Write tests that verify that switching from SDKv2 to the Framework does not affect provider behavior. +--- + +# Testing + +During migration, you should [write tests](#testing-migration) to verify that the behaviour of your provider has not +been altered by the migration itself. You will also need to [update](#provider-factories) your tests too. + +## Testing Migration + +During migration, we recommend writing tests to verify that switching from SDKv2 to the Framework has not affected your +provider's behavior. + +Use the `ExternalProviders` field within a `resource.TestStep` to specify the configuration of a specific provider to +use during each test step. You can specify a version of the provider built on SDKv2 during the first test step, and +then you can use the version of the provider built on the Framework in subsequent test step(s) to verify that Terraform +CLI does not detect any planned changes. + +You must also update the [provider factories](/plugin/framework/migrating/testing/provider-factories) to use +the Framework. + +### Example + +The following example is taken from +[v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http_test.go) +of the http provider. Some code is omitted for clarity (denoted by `...`). + +This example shows how you can use external providers to generate a state file with a previous version of the provider +and then verify that there are no planned changes after migrating to the Framework. + +- The first `TestStep` uses `ExternalProviders` to cause `terraform apply` to execute with `v2.2.0` of the http +provider, which is built on SDKv2. +- The second `TestStep` uses `ProtoV5ProviderFactories` so that the test uses the provider code contained within your +repository. The second step also uses `PlanOnly` to verify that a no-op plan is generated. + +```go +func TestDataSource_UpgradeFromVersion2_2_0(t *testing.T) { + ... + + resource.Test(t, resource.TestCase{ + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "http": { + VersionConstraint: "2.2.0", + Source: "hashicorp/http", + }, + }, + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), + ... + ), + }, + { + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), + PlanOnly: true, + }, + }, + }) +} +``` + +## Provider Factories +Existing tests should require minimal updates when migrating from SDKv2 to the Framework. The only critical change +relates to the provider factories that create the provider during the test case. + +We also recommend writing tests that verify that switching from SDKv2 to the Framework has not affected provider +behavior. Refer to [Testing Migration](#testing-migration) for details. + +### SDKv2 + +In SDKv2, you use the `ProviderFactories` field on the `resource.TestCase` struct to obtain `schema.Provider`. + +The following example shows a test written in SDKv2. + +```go +resource.UnitTest(t, resource.TestCase{ + ProviderFactories: testProviders(), +``` + +### Framework +In the Framework, use either the `ProtoV5ProviderFactories` or `ProtoV6ProviderFactories` field on the +`resource.TestCase` struct to obtain the `provider.Provider`, depending on the +[Terraform Plugin Protocol](/plugin/how-terraform-works#terraform-plugin-protocol) version your provider is using. +Refer to [Acceptance Tests - Specify Providers](/plugin/framework/acctests#specify-providers) in the Framework +documentation for details. + +The following example shows how you can write a test in the Framework for a Provider that uses protocol version 6. + +```go +resource.UnitTest(t, resource.TestCase{ + ProtoV6ProviderFactories: protoV6ProviderFactories(), +``` + +### Example +The following examples show how to migrate portions of the [http](https://github.com/hashicorp/terraform-provider-http) +provider. + +For clarity, some parts of the migration are omitted (denoted by `...`). To review a complete example, clone the +`terraform-provider-http` repository and compare +[v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source_test.go) with +[v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http_test.go). + + +**SDKv2** + +The following code sample is from the `data_source_http_test.go` file and shows how to define provider factories within +a test case when using SDKv2. + +```go +func TestDataSource_http200(t *testing.T) { + ... + + resource.UnitTest(t, resource.TestCase{ + ProviderFactories: testProviders(), + ... + }) +} +``` + +The following code sample is from the `provider_test.go` file and shows how to generate provider factories when using +SDKv2. + +```go +func testProviders() map[string]func() (*schema.Provider, error) { + return map[string]func() (*schema.Provider, error){ + "http": func() (*schema.Provider, error) { return New(), nil }, + } +} +``` + +**Framework** + +The following code sample is from the `data_source_http_test.go` file and shows how to define provider factories within +a test case when using the Framework. + +```go +func TestDataSource_200(t *testing.T) { + ... + + resource.UnitTest(t, resource.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + ... + }) +} +``` + +The following code sample is from the `provider_test.go` file and shows how to generate provider factories when using +the Framework. The call to `New` returns an instance of the provider. Refer to +[Provider Definition](/plugin/framework/migrating/provider#definition-framework) in the Framework documentation for +details. + +```go +func protoV5ProviderFactories() map[string]func() (tfprotov5.ProviderServer, error) { + return map[string]func() (tfprotov5.ProviderServer, error){ + "http": providerserver.NewProtocol5WithError(New()), + } +} +``` From 7e6158f01c700d680e44ef65f5f4b97cfaf73f17 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 26 Aug 2022 11:13:38 +0100 Subject: [PATCH 02/28] Apply suggestions from code review Co-authored-by: Robin Norwood --- .../attributes-blocks/blocks-computed.mdx | 6 ++-- .../migrating/attributes-blocks/blocks.mdx | 8 ++--- .../attributes-blocks/default-values.mdx | 2 +- .../migrating/attributes-blocks/fields.mdx | 6 ++-- .../migrating/attributes-blocks/force-new.mdx | 6 ++-- .../migrating/attributes-blocks/types.mdx | 11 +++---- .../framework/migrating/data-sources.mdx | 7 ++-- .../docs/plugin/framework/migrating/index.mdx | 32 +++++++++---------- .../plugin/framework/migrating/provider.mdx | 11 +++---- .../framework/migrating/resources/import.mdx | 6 ++-- .../migrating/resources/plan-modification.mdx | 4 +-- .../framework/migrating/resources/schema.mdx | 2 +- .../migrating/resources/state-upgrade.mdx | 2 +- .../plugin/framework/migrating/schema.mdx | 10 +++--- .../plugin/framework/migrating/testing.mdx | 5 ++- 15 files changed, 57 insertions(+), 61 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 8f32e4aab..18605acb6 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -38,7 +38,7 @@ map[string]*schema.Schema{ 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`. -When working with protocol version 6, it is recommended that computed blocks are represented using nested attributes. +When working with protocol version 6, we recommend that you define computed blocks using nested attributes. ```go map[string]tfsdk.Attribute{ @@ -54,7 +54,7 @@ map[string]tfsdk.Attribute{ ## 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 6 used [nested attributes](https://www.terraform.io/plugin/framework/schemas#attributes-1) when migrating blocks that are computed from SDKv2 to Framework. +- When using protocol version 6, use [nested attributes](https://www.terraform.io/plugin/framework/schemas#attributes-1) when migrating blocks that are computed from SDKv2 to Framework. ## Example @@ -69,7 +69,7 @@ with **SDKv2** -The following example from the `data_source_certificate.go` file shows the implementation of the `certificates` nested +The following example from the `data_source_certificate.go` file shows the implementation of the `certificates` nested block on the `certificate` data source's schema. ```go diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx index 7729dbc6f..8a07d8483 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx @@ -12,12 +12,12 @@ typically represent separate objects that are related to (or embedded within) th This page explains how to migrate nested blocks that are not computed (i.e., do not contain fields with `Computed: true`) from SDKv2 to the Framework. Refer to -[Computed Blocks](/plugin/framework/migrating/attributes-blocks/blocks-computed) if you are looking for information +[Computed Blocks](/plugin/framework/migrating/attributes-blocks/blocks-computed) for more details about migrating nested blocks that contain fields that are computed. ## Nested Block Example -The following example shows an example of a nested block in Terraform resource configuration. The `subject` nested +The following example shows a nested block in Terraform resource configuration. The `subject` nested block within the `tls_cert_request` resource configures the subject of a certificate request with the `common_name` and `organization` attributes. @@ -35,7 +35,7 @@ resource "tls_cert_request" "example" { ## SDKv2 -In SDKv2, blocks are defined by an attribute whose type is `TypeList`, or `TypeSet` and whose `Elem` field is set to a +In SDKv2, blocks are defined by an attribute whose type is `TypeList` or `TypeSet` and whose `Elem` field is set to a `schema.Resource` that contains a map of the block's attribute names to corresponding `schemaSchema` structs. ```go @@ -117,7 +117,7 @@ map[string]*schema.Schema{ **Framework** -The following example from the `resource_cert_request.go` file shows how the `subject` nested `subject` block on the +The following example from the `resource_cert_request.go` file shows how the nested `subject` block on the `cert_request` resource is defined with the Framework after the migration. ```go diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx index bae0c6e27..b31d62bd8 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx @@ -38,7 +38,7 @@ func resourceExample() *schema.Resource { ## Framework -In the Framework, you set default values with the `AttributePlanModifier` field on your attribute's `tfsdk.Attribute` +In the Framework, you set default values with the `PlanModifiers` field on your attribute's `tfsdk.Attribute` struct. ```go diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx index 8d797413d..84a93d7e6 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx @@ -1,12 +1,12 @@ --- page_title: 'Attributes flags: Migrating from SDKv2 to the Framework' description: >- - Migrate attribute required, optional, computed and sensitive from SDKv2 to the plugin Framework + Migrate attribute required, optional, computed, and sensitive fields from SDKv2 to the plugin Framework --- # Attribute Fields -A subset of attribute fields, such as required, optional, computed, or sensitive, define attribute behaviour. Refer to +A subset of attribute fields, such as required, optional, computed, or sensitive, define attribute behavior as boolean flags. Refer to [Schemas - Attributes](/plugin/framework/schemas#required) in the Framework documentation for details. This page explains how to migrate the required, optional, computed, and sensitive attribute fields from SDKv2 to the @@ -24,7 +24,7 @@ func resourceExample() *schema.Resource { "attribute_example": { Required bool Optional bool - Computed bool + Computed bool Sensitive bool ... ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx index 5d664934b..bdf0cac3f 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx @@ -1,12 +1,12 @@ --- page_title: 'Attribute ForceNew triggers: Migrating from SDKv2 to the Framework' description: >- - Migrate attribute force new in SDKv2 to attribute plan modifier in the Framework. + Migrate attribute force new in SDKv2 to an attribute plan modifier in the Framework. --- # ForceNew -In Terraform, sometimes a resource must be replaced when the value of an attribute changes. In SDKv2, this is +In Terraform, sometimes a resource must be replaced when the value of an attribute changes. In SDKv2, this is accomplished via the `ForceNew` field. In the Framework, you implement the same behavior via a `RequiresReplace` plan modifier. Refer to [Plan Modification - Attribute Plan Modification](/plugin/framework/resources/plan-modification#attribute-plan-modification) @@ -50,7 +50,7 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia Remember the following differences between SDKv2 and the Framework when completing the migration. - In both SDKv2 and Framework, `ForceNew` and `RequiresReplace`, respectively, only trigger a replace if the attribute -is not computed. In the Framework, if an attribute which is computed requires that the resource is replaced when it is +is not computed. In the Framework, if an attribute which is computed requires that the resource be replaced when it is changed, implement a plan modifier that triggers the replacement. Refer to [RequiresReplacePlanModifier](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/planmodifiers/attribute.go#L63) for an example, but bear in mind that each implementation requires different logic and you may need to detect whether diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index bf9bc01bc..ee6737877 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -7,13 +7,12 @@ description: >- # Attribute Types An attribute either contains a primitive type, such as an integer or a string, or contains other attributes. Attributes -that contain other attributes are referred to as nested attributes and are implemented by populating the +that contain other attributes are referred to as nested attributes, and are implemented by populating the `NestedAttributes` field on the `tfsdk.Attribute` struct. Refer to [Schemas - Attributes](/plugin/framework/schemas#type) in the Framework documentation for details. -This page explains how to migrate a primitive attribute from SDKv2 to the plugin Framework. There is an example of -migrating a nested block to a nested attribute in -[Provider Schema](/plugin/framework/migrating/provider#example-1) +This page explains how to migrate a primitive attribute from SDKv2 to the plugin Framework. For an example of +migrating a nested block to a nested attribute, refer to [Provider Schema](/plugin/framework/migrating/provider#example-1) in the Framework documentation. ## SDKv2 @@ -50,8 +49,8 @@ Remember the following differences between SDKv2 and the Framework when completi - In the Framework, an `Attribute` struct has either the `Type` or `Attributes` field defined. The `Type` field is set to a primitive type such as an integer or string, and you use the `Attributes` field for `NestedAttributes`. Refer to [Provider Schema](/plugin/framework/migrating/provider#example-1) for an example of using a single -nested attribute. Nested attributes are also described in more detail in the -[docs](https://www.terraform.io/plugin/framework/schemas#attributes-1). +nested attribute. Nested attributes are also described in more detail on the +[Schemas](https://www.terraform.io/plugin/framework/schemas#attributes-1) page in the Framework documentation. ## Example diff --git a/website/docs/plugin/framework/migrating/data-sources.mdx b/website/docs/plugin/framework/migrating/data-sources.mdx index 9414f6d14..7529080f7 100644 --- a/website/docs/plugin/framework/migrating/data-sources.mdx +++ b/website/docs/plugin/framework/migrating/data-sources.mdx @@ -42,21 +42,20 @@ schema.Resource { Timeouts: *ResourceTimeout, Description: string, } - ``` ## Framework -In the Framework, you define your provider's data sources adding them to your provider's `GetDataSources` function. +In the Framework, you define data sources adding them to your provider's `GetDataSources` function. -The `GetDataSources` function on your `provider.Provider` returns a map from the data source name (string) to a struct +The `GetDataSources` function on your `provider.Provider` returns a map from the data source name (string) to a type that implements the `DataSourceType` interface for each data source your provider supports. The following code shows how you add a data source to your provider with the Framework. ```go func (p *provider) GetDataSources(ctx context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { - return map[string]provider.ResourceType{}, nil + return map[string]provider.DataSourceType{}, nil } ``` diff --git a/website/docs/plugin/framework/migrating/index.mdx b/website/docs/plugin/framework/migrating/index.mdx index 83d32e682..4e0b692cb 100644 --- a/website/docs/plugin/framework/migrating/index.mdx +++ b/website/docs/plugin/framework/migrating/index.mdx @@ -14,7 +14,7 @@ In addition to this migration guide, we recommend referring to the main [Framewo ## Requirements -Before you migrate your provider to the Framework, ensure it meets the the following requirements: +Before you migrate your provider to the Framework, ensure it meets the following requirements: - Go 1.18+ - Built on the latest version of SDKv2 @@ -32,19 +32,19 @@ As you complete the migration, we recommend that you follow Test Driven Developm Take the following steps when you migrate a provider from SDKv2 to the Framework: -* Ensure all [tests](/plugin/framework/migrating/testing#testing-migration) pass. -* [Serve the provider](/plugin/framework/migrating/provider#serving-the-provider) via the Framework. -* Implement [muxing](/plugin/framework/migrating/provider#muxing) if you plan to migrate the provider over several point releases. -* Update the [provider definition](/plugin/framework/migrating/provider#provider-definition) to use the Framework. -* Update the [provider schema](/plugin/framework/migrating/provider#provider-schema). -* Update each of the provider's resources and data sources. -* Update related [tests](/plugin/framework/migrating/testing) to use the Framework, and ensure that the tests fail. -* Update the [resource](/plugin/framework/migrating/resources#schema) or [data source](/plugin/framework/migrating/data-sources) schema. -* Update the resource or data source [CRUD function](/plugin/framework/migrating/resources/crud) definitions. -* Update the resource or data source's [attribute schema](/plugin/framework/migrating/attributes/schema). -* Update the resource's [import function](/plugin/framework/migrating/resources/import), if your provider implements importing the resource. -* Implement [plan modification](/plugin/framework/migrating/resources/plan-modification) if needed. -* Implement [state upgraders](/plugin/framework/migrating/resources/plan-modification) if needed. -* Verify that related tests now pass. -* If you used muxing, remove the muxing configuration. +- Ensure all [tests](/plugin/framework/migrating/testing#testing-migration) pass. +- [Serve the provider](/plugin/framework/migrating/provider#serving-the-provider) via the Framework. + - Implement [muxing](/plugin/framework/migrating/provider#muxing) if you plan to migrate the provider over several point releases. +- Update the [provider definition](/plugin/framework/migrating/provider#provider-definition) to use the Framework. +- Update the [provider schema](/plugin/framework/migrating/provider#provider-schema). +- Update each of the provider's resources and data sources. + - Update related [tests](/plugin/framework/migrating/testing) to use the Framework, and ensure that the tests fail. + - Update the [resource](/plugin/framework/migrating/resources#schema) or [data source](/plugin/framework/migrating/data-sources) schema. + - Update the resource or data source's [CRUD function](/plugin/framework/migrating/resources/crud) definitions. + - Update the resource or data source's [attribute schema](/plugin/framework/migrating/attributes/schema). + - Update the resource's [import function](/plugin/framework/migrating/resources/import), if your provider supports importing the resource. + - Implement [plan modification](/plugin/framework/migrating/resources/plan-modification) if needed. + - Implement [state upgraders](/plugin/framework/migrating/resources/plan-modification) if needed. + - Verify that related tests now pass. +- If you used muxing, remove the muxing configuration. diff --git a/website/docs/plugin/framework/migrating/provider.mdx b/website/docs/plugin/framework/migrating/provider.mdx index fb8814362..17b2171c7 100644 --- a/website/docs/plugin/framework/migrating/provider.mdx +++ b/website/docs/plugin/framework/migrating/provider.mdx @@ -13,7 +13,7 @@ This page explains how to migrate a provider server, definition, and schema from ## Serving the Provider -You must update your provider's `main.go` file to serve Framework providers. +You must update your provider's `main.go` file to serve Framework providers. Refer to [Provider Servers](/plugin/framework/provider-servers) in the Framework documentation for details. ### SDKv2 @@ -105,7 +105,7 @@ func main() { ## Provider Definition Providers built with SDKv2 use a `schema.Provider` struct to define their behavior, while Framework providers use a -type that implements the `provider.Provider` interface, which you must define. +type that implements the `provider.Provider` interface, which you must define. Refer to [Providers](/plugin/framework/providers) in the Framework documentation for details. ### SDKv2 @@ -138,10 +138,9 @@ func New() *schema.Provider { ### Framework In the Framework, the second argument to your `provider.Serve` function requires a function that returns a type -satisfying the `provider.Provider` interface. Refer to [Providers](/plugin/framework/providers) in the Framework -documentation for details. +satisfying the `provider.Provider` interface. -The following ocde shows a typical implementation. In this implementation, the `GetResources` function returns a map +The following code shows a typical implementation. In this implementation, the `GetResources` function returns a map from resource names (strings) to types that implement the `provider.ResourceType` interface. The `GetDataSources` function returns a map from data source names (strings) to types that implement the `provider.DataSourceType` interface. Refer to the [Resources](/plugin/framework/migrating/resources) and @@ -292,7 +291,7 @@ func New() *schema.Provider { ``` ### Framework -In the Framework, the `GetSchema` function returns the provider schema. The `GetSchema` function is part of the +In the Framework, the `GetSchema` function returns the provider schema. The `GetSchema` function is part of the `provider.Provider` interface that your provider must implement. `GetSchema` returns a struct containing fields for `Attributes` and `Blocks`. These `Attributes and `Blocks` contain `map[string]tfsdk.Attribute` and `map[string]tfsdk.Block`, respectively. Refer to [Providers - GetSchema](/plugin/framework/providers#getschema) in the diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index 8560e3bb8..d55640b59 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -29,13 +29,13 @@ func resourceExample() *schema.Resource { ... ``` -The `StateContextFunc` is the function called to import a resource into Terraform state. Any operations requiredthat +The `StateContextFunc` is the function called to import a resource into Terraform state. Any operations that are needed to import the resource take place within this function and may result in the mutation of the `ResourceData` that is passed into the function. The return value is a slice of `schema.ResourceData`. This slice, which may be simple, likemight be as simple as returning the `ResourceData` that was passed into the function, or it may involve multipleinvolve fan out to multiple resources, such as AWS security groups. ## Framework -In the Framework, you implement the `ResourceWithImportState` interface on your `resource.Resource` type to allow +In the Framework, you implement the `ResourceWithImportState` interface on your `resource.Resource` type to allow users to import a given resource. The following code shows how you define an `ImportState` function with the Framework. The body of the `ImportState` @@ -134,7 +134,7 @@ func importPasswordFunc(ctx context.Context, d *schema.ResourceData, meta interf **Framework** The following shows the same section of provider code after the migration. -This code implements the `ResourceWithImportState` interface on the `passwordResource` type by defining an`ImportState` +This code implements the `ResourceWithImportState` interface on the `passwordResource` type by defining an `ImportState` function. ```go diff --git a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx index 4aafebe71..a4ea1ee69 100644 --- a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx +++ b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx @@ -29,8 +29,8 @@ func resourceExample() *schema.Resource { In the Framework, you implement plan modification either by implementing the `ResourceWithModifyPlan` interface on your resource type, or by implementing `PlanModifiers` on individual attributes. This page demonstrates how to implement the `Plan Modifiers` on individual attributes. Refer to -[Attributes - Default Values](/plugin/framework/migrating//attributes#default-values) and -[Attributes - Force New](/plugin/framework/migrating//attributes#force-new) in this guide for further information on how +[Attributes - Default Values](/plugin/framework/migrating/attributes#default-values) and +[Attributes - Force New](/plugin/framework/migrating/attributes#force-new) in this guide for further information on how to implement a plan modifier on an attribute. The `ResourceWithModifyPlan` interface requires a `ModifyPlan` function. diff --git a/website/docs/plugin/framework/migrating/resources/schema.mdx b/website/docs/plugin/framework/migrating/resources/schema.mdx index ec353427a..c15f991c4 100644 --- a/website/docs/plugin/framework/migrating/resources/schema.mdx +++ b/website/docs/plugin/framework/migrating/resources/schema.mdx @@ -130,7 +130,7 @@ Remember the following differences between SDKv2 and the Framework when completi - SDKv2 uses `schema.Resource` structs to define resources. These structs have a `Schema` field which holds a `schema.Schema` to define the resource's attributes and behavior. In the Framework, you define a type that implements -the `ResourceType` interface, which includes `GetSchema` that returns your resource's schema. +the `ResourceType` interface, which includes a `GetSchema` function that returns your resource's schema. - SDKv2 implements a resource's CRUD operations as functions on the `schema.Resource`. In the Framework, you define a type that implements the `Resource` interface. The resource interface contains the functions that define your resource's CRUD operations. diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx index 2b7e867da..3410de77b 100644 --- a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -7,7 +7,7 @@ description: >- # State Upgraders -When you update the resource implementation in your provider, some changes may not be compatible with old versions. You +When you update a resource's implementation in your provider, some changes may not be compatible with old versions. You can create state upgraders to help users migrate resources provisioned with old schema configurations. Refer to [State Upgrade](/plugin/framework/resources/state-upgrade) in the Framework documentation for details. diff --git a/website/docs/plugin/framework/migrating/schema.mdx b/website/docs/plugin/framework/migrating/schema.mdx index 6aa6bedfb..93a85a859 100644 --- a/website/docs/plugin/framework/migrating/schema.mdx +++ b/website/docs/plugin/framework/migrating/schema.mdx @@ -7,7 +7,8 @@ description: >- # Schema Providers, resources, and data sources all use schema to define their attributes and behavior. Schemas specify the -constraints of Terraform configuration blocks and how the provider, resource, or data source behaves. +constraints of Terraform configuration blocks and how the provider, resource, or data source behaves. Refer to +[Schemas](/plugin/framework/schemas) in the Framework documentation for details. This page explains the differences between the schema used by SDKv2 and the Framework. @@ -88,7 +89,6 @@ type Schema struct { In the Framework, you implement `GetSchema` functions for your provider, resources, and data sources. This function is required by the `provider.Provider`, `provider.ResourceType`, and `provider.DataSourceType` interfaces, respectively. -Refer to [Schemas](/plugin/framework/schemas) in the Framework documentation for details. The following code shows how you define the `GetSchema` function for your provider, resources, and data sources. @@ -124,7 +124,7 @@ type Schema struct { ``` You use the `Attributes` field to define attributes for your provider, resources, and data sources. You use the -`Blocks` to define named blocks. +`Blocks` field to define named blocks. ## Migration Notes Remember the following differences between SDKv2 and the Framework when completing the migration. @@ -137,6 +137,6 @@ provider and each resource and data type have a `GetSchema` function that return [attributes](/plugin/framework/migrating/attributes-and-blocks/attribute-schema) and [blocks](/plugin/framework/migrating/attributes-and-blocks/blocks) for your provider and each resource and data source. -- When you populate the `Version` field in `tfsdk.Schema` for a resource in the Framework, you copy the `Version` -field in `schema.Schema` in SDKv2. +- When you populate the `Version` field in `tfsdk.Schema` for a resource in the Framework, copy the `Version` +field in `schema.Schema` from the SDKv2 version of that resource. diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index 481b92781..e5e2c7a74 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -73,7 +73,8 @@ func TestDataSource_UpgradeFromVersion2_2_0(t *testing.T) { ## Provider Factories Existing tests should require minimal updates when migrating from SDKv2 to the Framework. The only critical change -relates to the provider factories that create the provider during the test case. +relates to the provider factories that create the provider during the test case. Refer to [Acceptance Tests - Specify Providers](/plugin/framework/acctests#specify-providers) in the Framework +documentation for details. We also recommend writing tests that verify that switching from SDKv2 to the Framework has not affected provider behavior. Refer to [Testing Migration](#testing-migration) for details. @@ -93,8 +94,6 @@ resource.UnitTest(t, resource.TestCase{ In the Framework, use either the `ProtoV5ProviderFactories` or `ProtoV6ProviderFactories` field on the `resource.TestCase` struct to obtain the `provider.Provider`, depending on the [Terraform Plugin Protocol](/plugin/how-terraform-works#terraform-plugin-protocol) version your provider is using. -Refer to [Acceptance Tests - Specify Providers](/plugin/framework/acctests#specify-providers) in the Framework -documentation for details. The following example shows how you can write a test in the Framework for a Provider that uses protocol version 6. From 45718ceef649098872ac7cc344a4d5012a2f37c5 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 26 Aug 2022 12:05:25 +0100 Subject: [PATCH 03/28] Further fixes following review (#459) --- .../migrating/attributes-blocks/fields.mdx | 16 ++++++++-------- .../framework/migrating/resources/schema.mdx | 4 ++-- .../migrating/resources/state-upgrade.mdx | 2 +- .../docs/plugin/framework/migrating/testing.mdx | 7 ++++--- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx index 84a93d7e6..fc123d5f5 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx @@ -22,10 +22,10 @@ func resourceExample() *schema.Resource { ... Schema: map[string]*schema.Schema{ "attribute_example": { - Required bool - Optional bool - Computed bool - Sensitive bool + Required: bool + Optional: bool + Computed: bool + Sensitive: bool ... ``` @@ -39,10 +39,10 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia ... Attributes: map[string]tfsdk.Attribute{ "attribute_example": { - Required bool - Optional bool - Computed bool - Sensitive bool + Required: bool + Optional: bool + Computed: bool + Sensitive: bool ... ``` diff --git a/website/docs/plugin/framework/migrating/resources/schema.mdx b/website/docs/plugin/framework/migrating/resources/schema.mdx index c15f991c4..be3d760c1 100644 --- a/website/docs/plugin/framework/migrating/resources/schema.mdx +++ b/website/docs/plugin/framework/migrating/resources/schema.mdx @@ -121,8 +121,8 @@ type Schema struct { } ``` -Refer to the [Resources - CRUD functions](FIXME: link) page in this guide to learn how to define the `resource.Resource` -type returned by your resource type's `NewResource` function. +Refer to the [Resources - CRUD functions](/plugin/framework/migrating/resources/crud) page in this guide to learn how to +implement the `resource.Resource` interface returned by your resource type's `NewResource` function. ## Migration Notes diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx index 3410de77b..53ebf611f 100644 --- a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -35,7 +35,7 @@ func resourceExample() *schema.Resource { ## Framework -In the Framework, you implement the `ResourceWithUpgradeState` on your `resource.Resource` type to upgrade your +In the Framework, you implement the `ResourceWithUpgradeState` interface on your resource to upgrade your resource's state when required. The following code shows how you define an `UpgradeState` function with the Framework. The body of the `UpgradeState` diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index e5e2c7a74..937c7ebf4 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -12,7 +12,9 @@ been altered by the migration itself. You will also need to [update](#provider-f ## Testing Migration During migration, we recommend writing tests to verify that switching from SDKv2 to the Framework has not affected your -provider's behavior. +provider's behavior. These tests use identical configuration. The first test step applies a plan and generates state +with the SDKv2 version of the provider. The second test step generates a plan with the Framework version of the provider +and verifies that the plan is a no-op indicating that migrating to the framework has not altered behaviour. Use the `ExternalProviders` field within a `resource.TestStep` to specify the configuration of a specific provider to use during each test step. You can specify a version of the provider built on SDKv2 during the first test step, and @@ -157,8 +159,7 @@ func TestDataSource_200(t *testing.T) { The following code sample is from the `provider_test.go` file and shows how to generate provider factories when using the Framework. The call to `New` returns an instance of the provider. Refer to -[Provider Definition](/plugin/framework/migrating/provider#definition-framework) in the Framework documentation for -details. +[Provider Definition](/plugin/framework/migrating/provider#provider-definition) in this guide for details. ```go func protoV5ProviderFactories() map[string]func() (tfprotov5.ProviderServer, error) { From 92724f46168362ac344474de9d601253379a5337 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 30 Aug 2022 09:58:18 +0100 Subject: [PATCH 04/28] Apply suggestions from code review Co-authored-by: Brian Flad --- website/data/plugin-framework-nav-data.json | 2 +- .../framework/migrating/attributes-blocks/blocks-computed.mdx | 2 +- .../plugin/framework/migrating/attributes-blocks/blocks.mdx | 2 +- .../plugin/framework/migrating/attributes-blocks/fields.mdx | 2 +- website/docs/plugin/framework/migrating/index.mdx | 2 +- website/docs/plugin/framework/migrating/schema.mdx | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/website/data/plugin-framework-nav-data.json b/website/data/plugin-framework-nav-data.json index d15c481de..dfd144edf 100644 --- a/website/data/plugin-framework-nav-data.json +++ b/website/data/plugin-framework-nav-data.json @@ -81,7 +81,7 @@ "path": "debugging" }, { - "title": "Migrating", + "title": "Migrating from SDK", "routes": [ { "title": "Overview", diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 18605acb6..92af56d3d 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -4,7 +4,7 @@ description: >- Migrate computed blocks from SDKv2 to attribute validators in the plugin Framework. --- -# Blocks with Computed Fields +# Computed Blocks Some providers, resources, and data sources include repeatable nested blocks in their attributes. Some blocks contain fields with `Computed: true`, which means that the provider code can define the value or that it could come from the diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx index 8a07d8483..301073394 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx @@ -10,7 +10,7 @@ Some providers, resources, and data sources include repeatable nested blocks in typically represent separate objects that are related to (or embedded within) the containing object. Refer to [Schemas - Blocks](/plugin/framework/FIXME) in the Framework documentation for details. -This page explains how to migrate nested blocks that are not computed (i.e., do not contain fields with +This page explains how to migrate nested blocks that are not computed (i.e., do not set `Computed: true`) from SDKv2 to the Framework. Refer to [Computed Blocks](/plugin/framework/migrating/attributes-blocks/blocks-computed) for more details about migrating nested blocks that contain fields that are computed. diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx index fc123d5f5..e6dd70493 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx @@ -1,5 +1,5 @@ --- -page_title: 'Attributes flags: Migrating from SDKv2 to the Framework' +page_title: 'Attribute Fields: Migrating from SDKv2 to the Framework' description: >- Migrate attribute required, optional, computed, and sensitive fields from SDKv2 to the plugin Framework --- diff --git a/website/docs/plugin/framework/migrating/index.mdx b/website/docs/plugin/framework/migrating/index.mdx index 4e0b692cb..50eed708a 100644 --- a/website/docs/plugin/framework/migrating/index.mdx +++ b/website/docs/plugin/framework/migrating/index.mdx @@ -8,7 +8,7 @@ description: >- This guide helps you migrate a Terraform provider from SDKv2 to the plugin Framework. We recommend migrating because the Framework has abstractions that make it easier to use, and it represents the future of Terraform plugin development. Refer to [Which SDK should I Use?](/plugin/which-sdk) for more details. -This guide provides information and examples for most common use cases, but it does not discuss every nuance of migration. You can ask additional migration questions in the [HashiCorp Discuss forum](https://discuss.hashicorp.com/c/terraform-providers/31). To request additions or updates to this guide, submit issues or pull requests to the [`terraform-plugin-framework` repository](https://github.com/hashicorp/terraform-plugin-framework). +This guide provides information and examples for most common use cases, but it does not discuss every nuance of migration. You can ask additional migration questions in the [HashiCorp Discuss forum](https://discuss.hashicorp.com/c/terraform-providers/tf-plugin-sdk/43). To request additions or updates to this guide, submit issues or pull requests to the [`terraform-plugin-framework` repository](https://github.com/hashicorp/terraform-plugin-framework). In addition to this migration guide, we recommend referring to the main [Framework documentation](/plugin/framework) as you migrate your provider. diff --git a/website/docs/plugin/framework/migrating/schema.mdx b/website/docs/plugin/framework/migrating/schema.mdx index 93a85a859..11ca5cdc7 100644 --- a/website/docs/plugin/framework/migrating/schema.mdx +++ b/website/docs/plugin/framework/migrating/schema.mdx @@ -1,5 +1,5 @@ --- -page_title: 'Provider: Migrating from SDKv2 to the Framework' +page_title: 'Schema: Migrating from SDKv2 to the Framework' description: >- Migrate a schema from SDKv2 to the plugin Framework. --- From 2cf4cf617cdf6cd3459c7710364e625b52434543 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 30 Aug 2022 10:25:49 +0100 Subject: [PATCH 05/28] Linting (#459) --- .../attributes-blocks/attribute-schema.mdx | 74 +++--- .../attributes-blocks/blocks-computed.mdx | 70 ++--- .../migrating/attributes-blocks/blocks.mdx | 96 +++---- .../attributes-blocks/default-values.mdx | 94 +++---- .../migrating/attributes-blocks/fields.mdx | 50 ++-- .../migrating/attributes-blocks/force-new.mdx | 64 ++--- .../migrating/attributes-blocks/types.mdx | 60 ++--- .../attributes-blocks/validators-custom.mdx | 86 +++---- .../validators-predefined.mdx | 134 +++++----- .../framework/migrating/data-sources.mdx | 82 +++--- .../docs/plugin/framework/migrating/index.mdx | 21 +- .../plugin/framework/migrating/provider.mdx | 240 +++++++++--------- .../framework/migrating/resources/crud.mdx | 180 ++++++------- .../framework/migrating/resources/import.mdx | 158 ++++++------ .../migrating/resources/plan-modification.mdx | 170 ++++++------- .../framework/migrating/resources/schema.mdx | 160 ++++++------ .../migrating/resources/state-upgrade.mdx | 212 ++++++++-------- .../plugin/framework/migrating/schema.mdx | 84 +++--- .../plugin/framework/migrating/testing.mdx | 95 +++---- 19 files changed, 1069 insertions(+), 1061 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx index c2190438b..675792348 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx @@ -21,9 +21,9 @@ the attribute's schema is omitted (denoted by `...`). ```go func ProviderExample() *schema.Provider { - return &schema.Provider{ + return &schema.Provider{ Schema: map[string]*schema.Schema{ - ... + ... }, ``` @@ -31,10 +31,10 @@ In SDKv2, resource and data source attributes are defined the same way on their ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - ... - }, + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + ... + }, ``` ## Framework @@ -47,27 +47,27 @@ The following code shows how to define an attribute for a resource with the Fram ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "example": { - ... + return tfsdk.Schema{ + Attributes: map[string]tfsdk.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 + 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 @@ -91,25 +91,25 @@ and the `data_source_http.go` file in [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http.go). -**SDKv2** +### SDKv2 The following example from the `data_source.go` file shows the implementation of the `url` attribute for the `http` data source. ```go func dataSource() *schema.Resource { - return &schema.Resource{ - ... - Schema: map[string]*schema.Schema{ - "url": { - Description: "The URL for the request. Supported schemes are `http` and `https`.", - Type: schema.TypeString, - Required: true, - }, + return &schema.Resource{ + ... + Schema: map[string]*schema.Schema{ + "url": { + Description: "The URL for the request. Supported schemes are `http` and `https`.", + Type: schema.TypeString, + Required: true, + }, ``` -**Framework** +### Framework The following shows the same section of provider code after the migration. @@ -117,13 +117,13 @@ This code implements the `url` attribute for the `http` data source with the Fra ```go func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ + return tfsdk.Schema{ ... Attributes: map[string]tfsdk.Attribute{ - "url": { - Description: "The URL for the request. Supported schemes are `http` and `https`.", - Type: types.StringType, - Required: true, - }, + "url": { + Description: "The URL for the request. Supported schemes are `http` and `https`.", + Type: types.StringType, + Required: true, + }, ... ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 92af56d3d..0267634b9 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -22,15 +22,15 @@ In SDKv2, blocks are defined by an attribute whose type is `TypeList`, or `TypeS ```go map[string]*schema.Schema{ - "example": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "nested_example": { - Type: schema.TypeString, - Computed: true, - ... + "example": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "nested_example": { + Type: schema.TypeString, + Computed: true, + ... ``` ## Framework @@ -43,12 +43,12 @@ When working with protocol version 6, we recommend that you define computed bloc ```go map[string]tfsdk.Attribute{ "example": { - Computed: true, - Type: types.ListType{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_example": types.StringType, - ... + Computed: true, + Type: types.ListType{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_example": types.StringType, + ... ``` ## Migration Notes @@ -67,7 +67,7 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/data_source_certificate.go). -**SDKv2** +### SDKv2 The following example from the `data_source_certificate.go` file shows the implementation of the `certificates` nested block on the `certificate` data source's schema. @@ -75,18 +75,18 @@ block on the `certificate` data source's schema. ```go Schema: map[string]*schema.Schema{ "certificates": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "signature_algorithm": { - Type: schema.TypeString, - Computed: true, - ... - }, + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "signature_algorithm": { + Type: schema.TypeString, + Computed: true, + ... + }, ``` -**Framework** +### Framework The following shows the same section of provider code after the migration. @@ -95,13 +95,13 @@ This code defines the `certificates` block as an attribute on the `certificate` ```go map[string]tfsdk.Attribute{ - "certificates": { - Type: types.ListType{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "signature_algorithm": types.StringType, - }, - }, - Computed: true, - ... + "certificates": { + Type: types.ListType{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "signature_algorithm": types.StringType, + }, + }, + Computed: true, + ... ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx index 301073394..dbd31f59c 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx @@ -40,19 +40,19 @@ In SDKv2, blocks are defined by an attribute whose type is `TypeList` or `TypeSe ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - ... + return &schema.Resource{ + ... map[string]*schema.Schema{ - "example" = &schema.Schema{ - Type: schema.TypeList, - Optional: bool, - MaxItems: int, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "nested_example": { - Type: schema.TypeString, - Optional: bool, - ... + "example" = &schema.Schema{ + Type: schema.TypeList, + Optional: bool, + MaxItems: int, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "nested_example": { + Type: schema.TypeString, + Optional: bool, + ... ``` ## Framework @@ -64,16 +64,16 @@ In the Framework, you implement nested blocks with the `Blocks` field of your pr ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.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]tfsdk.Block{ + "example": { + NestingMode: tfsdk.BlockNestingModeList, + MaxItems: int, + Attributes: map[string]tfsdk.Attribute{ + "nested_example": { + Type: types.StringType, + Optional: bool + ... ``` @@ -88,16 +88,16 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c with the `resource_cert_request.go` file in [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/resource_cert_request.go). -**SDKv2** +### SDKv2 The following example from the `common_cert.go` file shows the implementation of the `subject` nested block on the `cert_request` resource's schema with SDKv2. ```go map[string]*schema.Schema{ - "private_key_pem": &schema.Schema{ + "private_key_pem": &schema.Schema{ Type: schema.TypeString, - ... + ... "subject" = &schema.Schema{ Type: schema.TypeList, @@ -106,39 +106,39 @@ map[string]*schema.Schema{ Schema: map[string]*schema.Schema{ "organization": { Type: schema.TypeString, - ... + ... }, "common_name": { Type: schema.TypeString, - ... + ... }, - ... + ... ``` -**Framework** +### Framework The following example from the `resource_cert_request.go` file shows how the nested `subject` block on the `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, - ... - }, + 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, + ... + }, ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx index b31d62bd8..5b1f81178 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx @@ -26,14 +26,14 @@ the body of the attribute's schema is omitted (denoted by `...`). ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - ... + return &schema.Resource{ + ... Schema: map[string]*schema.Schema{ "attribute_example": { - Default: 2048, - ... - }, - ... + Default: 2048, + ... + }, + ... ``` ## Framework @@ -43,12 +43,12 @@ struct. ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ + return tfsdk.Schema{ ... - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { PlanModifiers: []tfsdk.AttributePlanModifier{ - defaultValue(types.Bool{Value: true}), + defaultValue(types.Bool{Value: true}), ... ``` @@ -69,23 +69,23 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/resource_private_key.go) with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/resource_private_key.go). -**SDKv2** +### SDKv2 The following example from the `resource_private_key.go` file shows the implementation of the `Default` field for the `rsa_bits` attribute on the `tls_private_key` resource with SDKv2. ```go func resourcePrivateKey() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "rsa_bits": { - Default: 2048, - ... - }, - ... + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rsa_bits": { + Default: 2048, + ... + }, + ... ``` -**Framework** +### Framework The following shows the same section of code after the migration. @@ -93,18 +93,18 @@ This code implements the `PlanModifiers` field for the `rsa_bits` attribute with ```go func (rt *privateKeyResourceType) 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.Int64{Value: 2048}), - ... - }, - ... - }, - ... - }, - }, nil + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "rsa_bits": { + PlanModifiers: []tfsdk.AttributePlanModifier{ + attribute_plan_modifier.DefaultValue(types.Int64{Value: 2048}), + ... + }, + ... + }, + ... + }, + }, nil } ``` @@ -113,35 +113,35 @@ that the `rsa_bits` attribute uses. ```go func DefaultValue(v attr.Value) tfsdk.AttributePlanModifier { - return &defaultValueAttributePlanModifier{v} + return &defaultValueAttributePlanModifier{v} } type defaultValueAttributePlanModifier struct { - DefaultValue attr.Value + DefaultValue attr.Value } var _ tfsdk.AttributePlanModifier = (*defaultValueAttributePlanModifier)(nil) func (apm *defaultValueAttributePlanModifier) Description(ctx context.Context) string { - ... + ... } func (apm *defaultValueAttributePlanModifier) MarkdownDescription(ctx context.Context) string { - ... + ... } func (apm *defaultValueAttributePlanModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, res *tfsdk.ModifyAttributePlanResponse) { - // If the attribute configuration is not null, we are done here - if !req.AttributeConfig.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() { - return - } - - res.AttributePlan = apm.DefaultValue + // If the attribute configuration is not null, we are done here + if !req.AttributeConfig.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() { + return + } + + res.AttributePlan = apm.DefaultValue } ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx index e6dd70493..e4b9b5067 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx @@ -18,12 +18,12 @@ In SDKv2, `Required`, `Optional`, `Computed`, and `Sensitive` are boolean fields ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - ... + return &schema.Resource{ + ... Schema: map[string]*schema.Schema{ "attribute_example": { - Required: bool - Optional: bool + Required: bool + Optional: bool Computed: bool Sensitive: bool ... @@ -35,15 +35,15 @@ In the Framework, you set the same fields on the `tfsdk.Attribute` struct, with ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - ... - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { Required: bool Optional: bool Computed: bool Sensitive: bool - ... + ... ``` ## Example @@ -57,34 +57,34 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c and the `data_source_http.go` file in [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http.go). -**SDKv2** +### SDKv2 The following example from the `data_source.go` file shows how the `url` attribute on the `http` data source is set to be required with SDKv2. ```go func dataSource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "url": { - Required: true, - ... - }, - ... + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + Required: true, + ... + }, + ... ``` -**Framework** +### Framework The following example from the `data_source_http.go` file shows how the `url` attribute on the `http` data source is set to be required with the Framework. ```go func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "url": { - Required: true, - ... - }, - ... + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "url": { + Required: true, + ... + }, + ... ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx index bdf0cac3f..b239a5d3a 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx @@ -21,12 +21,12 @@ cycle) whenever the attribute's value is changed. ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - ... + return &schema.Resource{ + ... Schema: map[string]*schema.Schema{ "attribute_example": { - ForceNew true - ... + ForceNew true + ... ``` ## Framework @@ -36,13 +36,13 @@ attribute's `tfsdk.Attribute` struct. ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ + return tfsdk.Schema{ ... - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - ... + resource.RequiresReplace(), + ... ``` ## Migration Notes @@ -66,27 +66,27 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). -**SDKv2** +### SDKv2 The following example from the `resource_password.go` file shows the implementation of the `ForceNew` field of the `random_password` resource's `keepers` attribute with SDKv2. ```go func resourcePassword() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "keepers": { - ForceNew: true, - ... - }, - ... - }, - ... - } + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "keepers": { + ForceNew: true, + ... + }, + ... + }, + ... + } } ``` -**Framework** +### Framework The following shows the same section of provider code after the migration. @@ -95,16 +95,16 @@ The example does this using the `PlanModifiers` field within the `random_passwor ```go func (r *passwordResourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "keepers": { - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, - ... - }, - ... - }, - }, nil + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "keepers": { + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + ... + }, + ... + }, + }, nil } ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index ee6737877..480c43fdb 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -20,14 +20,14 @@ In SDKv2, attribute types are defined by the `Type` field on the attribute's `sc ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - ... - Schema: map[string]*schema.Schema{ - "attribute_example": { - Type: schema.TypeName, - ... - }, - ... + return &schema.Resource{ + ... + Schema: map[string]*schema.Schema{ + "attribute_example": { + Type: schema.TypeName, + ... + }, + ... ``` ## Framework @@ -35,13 +35,13 @@ In the Framework, you set your attribute's type with the `Type` field on your at ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ + return tfsdk.Schema{ ... - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { - Type: types.NameType, - ... - }, + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { + Type: types.NameType, + ... + }, ``` ## Migration Notes Remember the following differences between SDKv2 and the Framework when completing the migration. @@ -63,34 +63,34 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c and the `data_source_http.go` file in [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http.go). -**SDKv2** +### SDKv2 The following example from the `data_source.go` file shows the implementation of the type field of the `url` attribute for the `http` data source with SDKv2. ```go func dataSource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "url": { - Type: schema.TypeString, - ... - }, - ... + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + Type: schema.TypeString, + ... + }, + ... ``` -**Framework** +### Framework The following example from the `data_source_http.go` file shows how the type of the `url` attribute for the `http` data source is defined with the Framework after the migration. ```go func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "url": { - Type: types.StringType, - ... - }, - ... + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "url": { + Type: types.StringType, + ... + }, + ... ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx index 061e2dd03..9fe025513 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx @@ -23,8 +23,8 @@ greater than one. ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - ... + return &schema.Resource{ + ... Schema: map[string]*schema.Schema{ "attribute_example": { ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)), @@ -41,13 +41,13 @@ greater than one. ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ + return tfsdk.Schema{ ... - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { + Attributes: map[string]tfsdk.Attribute{ + "attribute_example": { Validators: []tfsdk.AttributeValidator{ - int64validator.AtLeast(1), - ... + int64validator.AtLeast(1), + ... ``` ## Migration Notes @@ -69,24 +69,24 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). -**SDKv2** +### SDKv2 The following example from the `resource_password.go` file shows the implementation of the `ValidateDiagFunc` field for the `random_password`'s `length` attribute to validate that it's value is at least 1 (greater than zero). ```go func resourcePassword() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "length": { - ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)), - }, - }, - } + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "length": { + ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)), + }, + }, + } } ``` -**Framework** +### Framework The following shows the same section of provider code after the migration. @@ -95,15 +95,15 @@ validator. ```go func (r *passwordResourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "length": { - Validators: []tfsdk.AttributeValidator{ - int64validator.AtLeast(1), - }, - }, - }, - }, nil + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "length": { + Validators: []tfsdk.AttributeValidator{ + int64validator.AtLeast(1), + }, + }, + }, + }, nil } ``` @@ -116,36 +116,36 @@ var _ tfsdk.AttributeValidator = atLeastValidator{} // atLeastValidator validates that an integer Attribute's value is at least a certain value. type atLeastValidator struct { - min int64 + min int64 } // Description describes the validation in plain text formatting. func (validator atLeastValidator) Description(_ context.Context) string { - return fmt.Sprintf("value must be at least %d", validator.min) + return fmt.Sprintf("value must be at least %d", validator.min) } // MarkdownDescription describes the validation in Markdown formatting. func (validator atLeastValidator) MarkdownDescription(ctx context.Context) string { - return validator.Description(ctx) + return validator.Description(ctx) } // Validate performs the validation. func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) { - i, ok := validateInt(ctx, request, response) + i, ok := validateInt(ctx, request, response) - if !ok { - return - } + if !ok { + return + } - if i < validator.min { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.AttributePath, - validator.Description(ctx), - fmt.Sprintf("%d", i), - )) + if i < validator.min { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.AttributePath, + validator.Description(ctx), + fmt.Sprintf("%d", i), + )) - return - } + return + } } // AtLeast returns an AttributeValidator which ensures that any configured @@ -156,8 +156,8 @@ func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.Va // // Null (unconfigured) and unknown (known after apply) values are skipped. func AtLeast(min int64) tfsdk.AttributeValidator { - return atLeastValidator{ - min: min, - } + return atLeastValidator{ + min: min, + } } ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx index 2d54dc604..0019b9606 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -22,12 +22,12 @@ In SDKv2, the `ConflictsWith`, `ExactlyOneOf`, `AtLeastOneOf`, and `RequiredWith ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - ... + return &schema.Resource{ + ... Schema: map[string]*schema.Schema{ "attribute_example": { ConflictsWith: []string - ExactlyOneOf: []string + ExactlyOneOf: []string AtLeastOneOf: []string RequiredWith: []string ... @@ -43,13 +43,13 @@ validators do not meet your needs, you must define ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ + return tfsdk.Schema{ ... - Attributes: map[string]tfsdk.Attribute{ + Attributes: map[string]tfsdk.Attribute{ "attribute_example": { Validators: []tfsdk.AttributeValidator{ - schemavalidator.ConflictsWith(), - ... + schemavalidator.ConflictsWith(), + ... ``` ## Migration Notes @@ -87,32 +87,32 @@ when the proxy's url is set through the environment. The example also uses the ` ```go func New() (*schema.Provider, error) { - return &schema.Provider{ - Schema: map[string]*schema.Schema{ - "proxy": { - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "url": { - ConflictsWith: []string{"proxy.0.from_env"}, - ... - }, - "username": { - RequiredWith: []string{"proxy.0.url"}, - ... - }, - "password": { - RequiredWith: []string{"proxy.0.username"}, - ... - }, - "from_env": { - ConflictsWith: []string{"proxy.0.url", "proxy.0.username", "proxy.0.password"}, + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "proxy": { + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + ConflictsWith: []string{"proxy.0.from_env"}, + ... + }, + "username": { + RequiredWith: []string{"proxy.0.url"}, + ... + }, + "password": { + RequiredWith: []string{"proxy.0.username"}, + ... + }, + "from_env": { + ConflictsWith: []string{"proxy.0.url", "proxy.0.username", "proxy.0.password"}, ... - }, - }, - }, - }, - }, - }, nil + }, + }, + }, + }, + }, + }, nil } ``` @@ -125,40 +125,40 @@ via the `Validators` field of the provider's `proxy` block's attribute schema. ```go func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "proxy": { - Attributes: map[string]tfsdk.Attribute{ - "url": { - Validators: []tfsdk.AttributeValidator{ - schemavalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), - }, - ... - }, - "username": { - Validators: []tfsdk.AttributeValidator{ - schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("url")), - }, - ... - }, - "password": { - Validators: []tfsdk.AttributeValidator{ - schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("username")), - }, - ... - }, - "from_env": { - Validators: []tfsdk.AttributeValidator{ - schemavalidator.ConflictsWith( - path.MatchRelative().AtParent().AtName("url"), - path.MatchRelative().AtParent().AtName("username"), - path.MatchRelative().AtParent().AtName("password"), - ), - }, - ... - }, - }, - }, - }, nil + return tfsdk.Schema{ + Blocks: map[string]tfsdk.Block{ + "proxy": { + Attributes: map[string]tfsdk.Attribute{ + "url": { + Validators: []tfsdk.AttributeValidator{ + schemavalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), + }, + ... + }, + "username": { + Validators: []tfsdk.AttributeValidator{ + schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("url")), + }, + ... + }, + "password": { + Validators: []tfsdk.AttributeValidator{ + schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("username")), + }, + ... + }, + "from_env": { + Validators: []tfsdk.AttributeValidator{ + schemavalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("url"), + path.MatchRelative().AtParent().AtName("username"), + path.MatchRelative().AtParent().AtName("password"), + ), + }, + ... + }, + }, + }, + }, nil } ``` diff --git a/website/docs/plugin/framework/migrating/data-sources.mdx b/website/docs/plugin/framework/migrating/data-sources.mdx index 7529080f7..e26c08cb2 100644 --- a/website/docs/plugin/framework/migrating/data-sources.mdx +++ b/website/docs/plugin/framework/migrating/data-sources.mdx @@ -23,9 +23,9 @@ The following example shows a typical implementation. The rest of the `schema.Pr ```go func New() *schema.Provider { - return &schema.Provider{ - DataSourcesMap: map[string]*schema.Resource{} - ... + return &schema.Provider{ + DataSourcesMap: map[string]*schema.Resource{} + ... }, ``` @@ -34,13 +34,13 @@ resource struct. For clarity, the example omits fields that are not available fo ```go schema.Resource { - Schema: map[string]*schema.Schema, - Read: ReadFunc, - ReadContext: ReadContextFunc, - ReadWithoutTimeout: ReadContextFunc, - DeprecationMessage: string, - Timeouts: *ResourceTimeout, - Description: string, + Schema: map[string]*schema.Schema, + Read: ReadFunc, + ReadContext: ReadContextFunc, + ReadWithoutTimeout: ReadContextFunc, + DeprecationMessage: string, + Timeouts: *ResourceTimeout, + Description: string, } ``` @@ -75,11 +75,11 @@ Framework. type dataSourceTypeExample struct{} func (r *dataSourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - ... + ... } func (r *dataSourceTypeExample) NewDataSource(ctx context.Context, p provider.Provider) (datasource.DataSource, diag.Diagnostics) { - ... + ... } ``` @@ -105,17 +105,17 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c and the `data_source_http.go` file in [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http.go). -**SDKv2** +### SDKv2 The following example from the `provider.go` file shows an implementation of the `DataSourcesMap` field on the provider schema with SDKv2. ```go func New() (*schema.Provider, error) { - return &schema.Provider { - DataSourcesMap: map[string]*schema.Resource { - "http": dataSource(), - ... + return &schema.Provider { + DataSourcesMap: map[string]*schema.Resource { + "http": dataSource(), + ... ``` The following example from the `data_source.go` file shows how the `ReadContext` function and `Schema` are defined for @@ -123,31 +123,31 @@ the `http` data source with SDKv2. ```go func dataSource() *schema.Resource { - return &schema.Resource{ - ReadContext: dataSourceRead, - - Schema: map[string]*schema.Schema{ - "url": { - Description: "The URL for the request. Supported schemes are `http` and `https`.", - Type: schema.TypeString, - Required: true, - }, - ... - }, - } + return &schema.Resource{ + ReadContext: dataSourceRead, + + Schema: map[string]*schema.Schema{ + "url": { + Description: "The URL for the request. Supported schemes are `http` and `https`.", + Type: schema.TypeString, + Required: true, + }, + ... + }, + } } ``` -**Framework** +### Framework The following example from the `provider.go` file shows how the `http` data source is defined with the Framework after the migration. ```go func (p *provider) GetDataSources(context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { - return map[string]provider.DataSourceType{ - "http": &httpDataSourceType{}, - }, nil + return map[string]provider.DataSourceType{ + "http": &httpDataSourceType{}, + }, nil } ``` @@ -156,14 +156,14 @@ Framework. ```go func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "url": { - Description: "The URL for the request. Supported schemes are `http` and `https`.", - Type: types.StringType, - Required: true, - }, - ... + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "url": { + Description: "The URL for the request. Supported schemes are `http` and `https`.", + Type: types.StringType, + Required: true, + }, + ... func (d *httpDataSourceType) NewDataSource(context.Context, provider.Provider) (datasource.DataSource, diag.Diagnostics) { return &httpDataSource{}, nil diff --git a/website/docs/plugin/framework/migrating/index.mdx b/website/docs/plugin/framework/migrating/index.mdx index 50eed708a..6b898e35b 100644 --- a/website/docs/plugin/framework/migrating/index.mdx +++ b/website/docs/plugin/framework/migrating/index.mdx @@ -19,7 +19,9 @@ Before you migrate your provider to the Framework, ensure it meets the following - Go 1.18+ - Built on the latest version of SDKv2 - The provider is for use with Terraform >= 0.12.0 + ## Muxing + Consider muxing when you need to migrate a provider that contains many resources or data sources. Muxing lets you use two versions of the same provider concurrently, with each serving different resources or data sources. This lets you migrate individual resources or data sources to the Framework one at a time. Refer to the [Combining and Translating documentation](/plugin/mux) for details about muxing configuration. @@ -34,17 +36,16 @@ Take the following steps when you migrate a provider from SDKv2 to the Framework - Ensure all [tests](/plugin/framework/migrating/testing#testing-migration) pass. - [Serve the provider](/plugin/framework/migrating/provider#serving-the-provider) via the Framework. - - Implement [muxing](/plugin/framework/migrating/provider#muxing) if you plan to migrate the provider over several point releases. + - Implement [muxing](/plugin/framework/migrating/provider#muxing) if you plan to migrate the provider over several point releases. - Update the [provider definition](/plugin/framework/migrating/provider#provider-definition) to use the Framework. - Update the [provider schema](/plugin/framework/migrating/provider#provider-schema). - Update each of the provider's resources and data sources. - - Update related [tests](/plugin/framework/migrating/testing) to use the Framework, and ensure that the tests fail. - - Update the [resource](/plugin/framework/migrating/resources#schema) or [data source](/plugin/framework/migrating/data-sources) schema. - - Update the resource or data source's [CRUD function](/plugin/framework/migrating/resources/crud) definitions. - - Update the resource or data source's [attribute schema](/plugin/framework/migrating/attributes/schema). - - Update the resource's [import function](/plugin/framework/migrating/resources/import), if your provider supports importing the resource. - - Implement [plan modification](/plugin/framework/migrating/resources/plan-modification) if needed. - - Implement [state upgraders](/plugin/framework/migrating/resources/plan-modification) if needed. - - Verify that related tests now pass. + - Update related [tests](/plugin/framework/migrating/testing) to use the Framework, and ensure that the tests fail. + - Update the [resource](/plugin/framework/migrating/resources#schema) or [data source](/plugin/framework/migrating/data-sources) schema. + - Update the resource or data source's [CRUD function](/plugin/framework/migrating/resources/crud) definitions. + - Update the resource or data source's [attribute schema](/plugin/framework/migrating/attributes/schema). + - Update the resource's [import function](/plugin/framework/migrating/resources/import), if your provider supports importing the resource. + - Implement [plan modification](/plugin/framework/migrating/resources/plan-modification) if needed. + - Implement [state upgraders](/plugin/framework/migrating/resources/plan-modification) if needed. + - Verify that related tests now pass. - If you used muxing, remove the muxing configuration. - diff --git a/website/docs/plugin/framework/migrating/provider.mdx b/website/docs/plugin/framework/migrating/provider.mdx index 17b2171c7..2c57b505b 100644 --- a/website/docs/plugin/framework/migrating/provider.mdx +++ b/website/docs/plugin/framework/migrating/provider.mdx @@ -23,14 +23,15 @@ The following code shows a basic implementation for serving an SDKv2 provider. ```go func main() { - plugin.Serve( - &plugin.ServeOpts{ - ProviderFunc: provider.New, - ProviderAddr: "registry.terraform.io//", - }, - ) + plugin.Serve( + &plugin.ServeOpts{ + ProviderFunc: provider.New, + ProviderAddr: "registry.terraform.io//", + }, + ) } ``` + ### Framework In the Framework, you serve your provider by calling `providerserver.Serve` in your provider package's `main` function. @@ -40,17 +41,17 @@ The following code shows an equivalent implementation for serving a provider in ```go func main() { - err := providerserver.Serve( - context.Background(), - provider.New, - providerserver.ServeOpts{ - Address: "registry.terraform.io//", - }, - ) - - if err != nil { - log.Fatal(err) - } + err := providerserver.Serve( + context.Background(), + provider.New, + providerserver.ServeOpts{ + Address: "registry.terraform.io//", + }, + ) + + if err != nil { + log.Fatal(err) + } } ``` @@ -68,37 +69,37 @@ mode. ```go func main() { - ctx := context.Background() + ctx := context.Background() - var debug bool + var debug bool - flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") - flag.Parse() + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() - providers := []func() tfprotov5.ProviderServer{ - providerserver.NewProtocol5(provider.New()), - tftime.Provider().GRPCProvider, - } + providers := []func() tfprotov5.ProviderServer{ + providerserver.NewProtocol5(provider.New()), + tftime.Provider().GRPCProvider, + } - muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...) - if err != nil { - log.Fatal(err) - } + muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...) + if err != nil { + log.Fatal(err) + } - var serveOpts []tf5server.ServeOpt + var serveOpts []tf5server.ServeOpt - if debug { - serveOpts = append(serveOpts, tf5server.WithManagedDebug()) - } + if debug { + serveOpts = append(serveOpts, tf5server.WithManagedDebug()) + } - err = tf5server.Serve( - "registry.terraform.io/hashicorp/time", - muxServer.ProviderServer, - serveOpts..., - ) - if err != nil { - log.Fatal(err) - } + err = tf5server.Serve( + "registry.terraform.io/hashicorp/time", + muxServer.ProviderServer, + serveOpts..., + ) + if err != nil { + log.Fatal(err) + } } ``` @@ -121,17 +122,17 @@ The following example shows a basic implementation of an SDKv2 provider. For cla ```go func New() *schema.Provider { - return &schema.Provider{ - Schema: map[string]*schema.Schema{}, + return &schema.Provider{ + Schema: map[string]*schema.Schema{}, ConfigureContextFunc: configureContextFunc(), - ResourcesMap: map[string]*schema.Resource{ - "resource_example": resourceExample(), - }, - DataSourcesMap: map[string]*schema.Resource{ - "dataSource_example": dataSourceExample(), - }, + ResourcesMap: map[string]*schema.Resource{ + "resource_example": resourceExample(), + }, + DataSourcesMap: map[string]*schema.Resource{ + "dataSource_example": dataSourceExample(), + }, ... - } + } } ``` @@ -176,6 +177,7 @@ func (p *provider) GetDataSources(ctx context.Context) (map[string]provider.Data ``` ### Migration Notes + Remember the following differences between SDKv2 and the Framework when completing the migration. - In SDKv2, your provider's `New` function returns a `schema.Provider` struct. In the Framework, `New` returns a type @@ -210,22 +212,22 @@ The following example shows how to set up a provider schema, configuration, reso ```go func New() (*schema.Provider, error) { - return &schema.Provider{ - Schema: map[string]*schema.Schema{ - "proxy": { - ... - }, - }, - ConfigureContextFunc: configureProvider, - ResourcesMap: map[string]*schema.Resource{ - "tls_private_key": resourcePrivateKey(), - ... - }, - DataSourcesMap: map[string]*schema.Resource{ - "tls_public_key": dataSourcePublicKey(), - ... - }, - }, nil + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "proxy": { + ... + }, + }, + ConfigureContextFunc: configureProvider, + ResourcesMap: map[string]*schema.Resource{ + "tls_private_key": resourcePrivateKey(), + ... + }, + DataSourcesMap: map[string]*schema.Resource{ + "tls_public_key": dataSourcePublicKey(), + ... + }, + }, nil } ``` @@ -237,35 +239,35 @@ The following shows the same section of provider code after the migration. var _ provider.Provider = (*provider)(nil) func New() provider.Provider { - return &provider{} + return &provider{} } func (p *provider) GetResources(_ context.Context) (map[string]provider.ResourceType, diag.Diagnostics) { - return map[string]provider.ResourceType{ - "tls_private_key": &privateKeyResourceType{}, - ... - }, nil + return map[string]provider.ResourceType{ + "tls_private_key": &privateKeyResourceType{}, + ... + }, nil } func (p *provider) GetDataSources(_ context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { - return map[string]provider.DataSourceType{ - "tls_public_key": &publicKeyDataSourceType{}, - ... - }, nil + return map[string]provider.DataSourceType{ + "tls_public_key": &publicKeyDataSourceType{}, + ... + }, nil } func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "proxy": { - ... - }, - }, - }, nil + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "proxy": { + ... + }, + }, + }, nil } func (p *provider) Configure(ctx context.Context, req provider.ConfigureRequest, res *provider.ConfigureResponse) { - ... + ... } ``` @@ -285,15 +287,16 @@ of the `schema.Provider` struct has been omitted for clarity (denoted by `...`). ```go func New() *schema.Provider { - return &schema.Provider{ - Schema: map[string]*schema.Schema{}, - ... + return &schema.Provider{ + Schema: map[string]*schema.Schema{}, + ... ``` + ### Framework In the Framework, the `GetSchema` function returns the provider schema. The `GetSchema` function is part of the `provider.Provider` interface that your provider must implement. `GetSchema` returns a struct containing fields for -`Attributes` and `Blocks`. These `Attributes and `Blocks` contain `map[string]tfsdk.Attribute` and +`Attributes` and `Blocks`. These `Attributes` and `Blocks` contain `map[string]tfsdk.Attribute` and `map[string]tfsdk.Block`, respectively. Refer to [Providers - GetSchema](/plugin/framework/providers#getschema) in the Framework documentation for details. @@ -301,7 +304,7 @@ The following code shows the `GetSchema` function, which returns the provider sc ```go func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{}, nil + return tfsdk.Schema{}, nil } ``` @@ -310,6 +313,7 @@ Refer to the [Attributes](/plugin/framework/migrating/attributes-blocks#attribut those fields to the Framework. ### Migration Notes + Remember the following differences between SDKv2 and the Framework when completing the migration. In SDKv2, `schema.Schema` is a struct that defines attributes and behaviors (e.g., `Type`, `Optional`). In the @@ -336,21 +340,21 @@ The following example shows how to configure an attribute within a provider sche ```go Schema: map[string]*schema.Schema{ - "proxy": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "url": { - Type: schema.TypeString, - Optional: true, - ValidateDiagFunc: validation.ToDiagFunc(validation.IsURLWithScheme(SupportedProxySchemesStr())), - ConflictsWith: []string{"proxy.0.from_env"}, - Description: "URL used to connect to the Proxy. " + - fmt.Sprintf("Accepted schemes are: `%s`. ", strings.Join(SupportedProxySchemesStr(), "`, `")), - }, - ... + "proxy": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validation.ToDiagFunc(validation.IsURLWithScheme(SupportedProxySchemesStr())), + ConflictsWith: []string{"proxy.0.from_env"}, + Description: "URL used to connect to the Proxy. " + + fmt.Sprintf("Accepted schemes are: `%s`. ", strings.Join(SupportedProxySchemesStr(), "`, `")), + }, + ... ``` **Framework** @@ -359,19 +363,19 @@ The following example shows how to configure provider schema using the Framework ```go func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "proxy": { - Optional: true, - Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ - "url": { - Type: types.StringType, - Optional: true, - Validators: []tfsdk.AttributeValidator{ - attribute_validator.UrlWithScheme(supportedProxySchemesStr()...), - schemavalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), - }, - MarkdownDescription: "URL used to connect to the Proxy. " + - fmt.Sprintf("Accepted schemes are: `%s`. ", strings.Join(supportedProxySchemesStr(), "`, `")), - }, + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "proxy": { + Optional: true, + Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "url": { + Type: types.StringType, + Optional: true, + Validators: []tfsdk.AttributeValidator{ + attribute_validator.UrlWithScheme(supportedProxySchemesStr()...), + schemavalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), + }, + MarkdownDescription: "URL used to connect to the Proxy. " + + fmt.Sprintf("Accepted schemes are: `%s`. ", strings.Join(supportedProxySchemesStr(), "`, `")), + }, ``` diff --git a/website/docs/plugin/framework/migrating/resources/crud.mdx b/website/docs/plugin/framework/migrating/resources/crud.mdx index cd2f81803..a60c2e1fb 100644 --- a/website/docs/plugin/framework/migrating/resources/crud.mdx +++ b/website/docs/plugin/framework/migrating/resources/crud.mdx @@ -23,12 +23,12 @@ The following code shows a basic implementation of CRUD functions with SDKv2. Fo ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - CreateContext: create, - ReadContext: read, - UpdateContext: update, - DeleteContext: delete, - ... + return &schema.Resource{ + CreateContext: create, + ReadContext: read, + UpdateContext: update, + DeleteContext: delete, + ... ``` ## Framework @@ -42,24 +42,24 @@ The following code shows how you define a `resource.Resource` which implements C ```go type resourceExample struct { - p provider + p provider } func (r *resourceExample) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - ... + ... } func (r *resourceExample) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - ... + ... } func (r *resourceExample) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - ... + ... } func (r *resourceExample) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - ... + ... } ``` ## Migration Notes @@ -86,7 +86,7 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). -**SDKv2** +### SDKv2 The following example from the `resource_password.go` file shows implementations of CRUD functions on the `random_password` resource with SDKv2. The `UpdateContext` function is not implemented because the provider does not @@ -94,11 +94,11 @@ support updating this resource. ```go func resourcePassword() *schema.Resource { - return &schema.Resource{ - CreateContext: createPassword, - ReadContext: readNil, - DeleteContext: RemoveResourceFromState, - ... + return &schema.Resource{ + CreateContext: createPassword, + ReadContext: readNil, + DeleteContext: RemoveResourceFromState, + ... ``` The following example shows the implementation of the `createPassword()` function with SDKv2. The implementations of @@ -106,87 +106,87 @@ the `readNil()` and `RemoveResourceFromState()` functions are not shown for brev ```go func createPassword(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - diags := createStringFunc(true)(ctx, d, meta) - if diags.HasError() { - return diags - } - - hash, err := generateHash(d.Get("result").(string)) - if err != nil { - diags = append(diags, diag.Errorf("err: %s", err)...) - return diags - } - - if err := d.Set("bcrypt_hash", hash); err != nil { - diags = append(diags, diag.Errorf("err: %s", err)...) - return diags - } - - return nil + diags := createStringFunc(true)(ctx, d, meta) + if diags.HasError() { + return diags + } + + hash, err := generateHash(d.Get("result").(string)) + if err != nil { + diags = append(diags, diag.Errorf("err: %s", err)...) + return diags + } + + if err := d.Set("bcrypt_hash", hash); err != nil { + diags = append(diags, diag.Errorf("err: %s", err)...) + return diags + } + + return nil } ``` -**Framework** +### Framework The following shows the same section of provider code after the migration. This code implements the `Create()` function for the `random_password` resource with the Framework. ```go func (r *passwordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan passwordModelV2 - - diags := req.Plan.Get(ctx, &plan) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - params := random.StringParams{ - Length: plan.Length.Value, - Upper: plan.Upper.Value, - MinUpper: plan.MinUpper.Value, - Lower: plan.Lower.Value, - MinLower: plan.MinLower.Value, - Numeric: plan.Numeric.Value, - MinNumeric: plan.MinNumeric.Value, - Special: plan.Special.Value, - MinSpecial: plan.MinSpecial.Value, - OverrideSpecial: plan.OverrideSpecial.Value, - } - - result, err := random.CreateString(params) - if err != nil { - resp.Diagnostics.Append(diagnostics.RandomReadError(err.Error())...) - return - } - - state := passwordModelV2{ - ID: types.String{Value: "none"}, - Keepers: plan.Keepers, - Length: types.Int64{Value: plan.Length.Value}, - Special: types.Bool{Value: plan.Special.Value}, - Upper: types.Bool{Value: plan.Upper.Value}, - Lower: types.Bool{Value: plan.Lower.Value}, - Numeric: types.Bool{Value: plan.Numeric.Value}, - MinNumeric: types.Int64{Value: plan.MinNumeric.Value}, - MinUpper: types.Int64{Value: plan.MinUpper.Value}, - MinLower: types.Int64{Value: plan.MinLower.Value}, - MinSpecial: types.Int64{Value: plan.MinSpecial.Value}, - OverrideSpecial: types.String{Value: plan.OverrideSpecial.Value}, - Result: types.String{Value: string(result)}, - } - - hash, err := generateHash(plan.Result.Value) - if err != nil { - resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) - } - - state.BcryptHash = types.String{Value: hash} - - diags = resp.State.Set(ctx, state) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } + var plan passwordModelV2 + + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + params := random.StringParams{ + Length: plan.Length.Value, + Upper: plan.Upper.Value, + MinUpper: plan.MinUpper.Value, + Lower: plan.Lower.Value, + MinLower: plan.MinLower.Value, + Numeric: plan.Numeric.Value, + MinNumeric: plan.MinNumeric.Value, + Special: plan.Special.Value, + MinSpecial: plan.MinSpecial.Value, + OverrideSpecial: plan.OverrideSpecial.Value, + } + + result, err := random.CreateString(params) + if err != nil { + resp.Diagnostics.Append(diagnostics.RandomReadError(err.Error())...) + return + } + + state := passwordModelV2{ + ID: types.String{Value: "none"}, + Keepers: plan.Keepers, + Length: types.Int64{Value: plan.Length.Value}, + Special: types.Bool{Value: plan.Special.Value}, + Upper: types.Bool{Value: plan.Upper.Value}, + Lower: types.Bool{Value: plan.Lower.Value}, + Numeric: types.Bool{Value: plan.Numeric.Value}, + MinNumeric: types.Int64{Value: plan.MinNumeric.Value}, + MinUpper: types.Int64{Value: plan.MinUpper.Value}, + MinLower: types.Int64{Value: plan.MinLower.Value}, + MinSpecial: types.Int64{Value: plan.MinSpecial.Value}, + OverrideSpecial: types.String{Value: plan.OverrideSpecial.Value}, + Result: types.String{Value: string(result)}, + } + + hash, err := generateHash(plan.Result.Value) + if err != nil { + resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) + } + + state.BcryptHash = types.String{Value: hash} + + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } } ``` diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index d55640b59..51476fc1c 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -22,11 +22,11 @@ for clarity (denoted by `...`). ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - Importer: &schema.ResourceImporter{ - StateContext: StateContextFunc, - }, - ... + return &schema.Resource{ + Importer: &schema.ResourceImporter{ + StateContext: StateContextFunc, + }, + ... ``` The `StateContextFunc` is the function called to import a resource into Terraform state. Any operations that @@ -72,19 +72,19 @@ with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/int This example also shows one way to handle populating attributes with their default values during import. -**SDKv2** +### SDKv2 The following example from the `resource_password.go` file shows the import function for the `random_password` resource with SDKv2. ```go func resourcePassword() *schema.Resource { - return &schema.Resource{ - Importer: &schema.ResourceImporter{ - StateContext: importPasswordFunc, - }, - ... - } + return &schema.Resource{ + Importer: &schema.ResourceImporter{ + StateContext: importPasswordFunc, + }, + ... + } } ``` @@ -92,46 +92,46 @@ The following example shows the implementation of the `importPasswordFunc` funct ```go func importPasswordFunc(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - for k, v := range passwordSchemaV2() { - if v.Default == nil { - continue - } - if err := d.Set(k, v.Default); err != nil { - return nil, fmt.Errorf("error setting %s: %w", k, err) - } - } - - for _, key := range []string{"number", "numeric"} { - if err := d.Set(key, true); err != nil { - return nil, fmt.Errorf("error setting %s: %w", key, err) - } - } - - val := d.Id() - d.SetId("none") - - if err := d.Set("result", val); err != nil { - return nil, fmt.Errorf("resource password import failed, error setting result: %w", err) - } - - if err := d.Set("length", len(val)); err != nil { - return nil, fmt.Errorf("error setting length: %w", err) - } - - hash, err := generateHash(val) - if err != nil { - return nil, fmt.Errorf("resource password import failed, generate hash error: %w", err) - } - - if err := d.Set("bcrypt_hash", hash); err != nil { - return nil, fmt.Errorf("resource password import failed, error setting bcrypt_hash: %w", err) - } - - return []*schema.ResourceData{d}, nil + for k, v := range passwordSchemaV2() { + if v.Default == nil { + continue + } + if err := d.Set(k, v.Default); err != nil { + return nil, fmt.Errorf("error setting %s: %w", k, err) + } + } + + for _, key := range []string{"number", "numeric"} { + if err := d.Set(key, true); err != nil { + return nil, fmt.Errorf("error setting %s: %w", key, err) + } + } + + val := d.Id() + d.SetId("none") + + if err := d.Set("result", val); err != nil { + return nil, fmt.Errorf("resource password import failed, error setting result: %w", err) + } + + if err := d.Set("length", len(val)); err != nil { + return nil, fmt.Errorf("error setting length: %w", err) + } + + hash, err := generateHash(val) + if err != nil { + return nil, fmt.Errorf("resource password import failed, generate hash error: %w", err) + } + + if err := d.Set("bcrypt_hash", hash); err != nil { + return nil, fmt.Errorf("resource password import failed, error setting bcrypt_hash: %w", err) + } + + return []*schema.ResourceData{d}, nil } ``` -**Framework** +### Framework The following shows the same section of provider code after the migration. This code implements the `ResourceWithImportState` interface on the `passwordResource` type by defining an `ImportState` @@ -139,35 +139,35 @@ function. ```go func (r *passwordResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - id := req.ID - - state := passwordModelV2{ - ID: types.String{Value: "none"}, - Result: types.String{Value: id}, - Length: types.Int64{Value: int64(len(id))}, - Special: types.Bool{Value: true}, - Upper: types.Bool{Value: true}, - Lower: types.Bool{Value: true}, - Numeric: types.Bool{Value: true}, - MinSpecial: types.Int64{Value: 0}, - MinUpper: types.Int64{Value: 0}, - MinLower: types.Int64{Value: 0}, - MinNumeric: types.Int64{Value: 0}, - } - - state.Keepers.ElemType = types.StringType - - hash, err := generateHash(id) - if err != nil { - resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) - } - - state.BcryptHash = types.String{Value: hash} - - diags := resp.State.Set(ctx, &state) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } + id := req.ID + + state := passwordModelV2{ + ID: types.String{Value: "none"}, + Result: types.String{Value: id}, + Length: types.Int64{Value: int64(len(id))}, + Special: types.Bool{Value: true}, + Upper: types.Bool{Value: true}, + Lower: types.Bool{Value: true}, + Numeric: types.Bool{Value: true}, + MinSpecial: types.Int64{Value: 0}, + MinUpper: types.Int64{Value: 0}, + MinLower: types.Int64{Value: 0}, + MinNumeric: types.Int64{Value: 0}, + } + + state.Keepers.ElemType = types.StringType + + hash, err := generateHash(id) + if err != nil { + resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) + } + + state.BcryptHash = types.String{Value: hash} + + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } } ``` diff --git a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx index a4ea1ee69..608de45d6 100644 --- a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx +++ b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx @@ -19,9 +19,9 @@ for clarity (denoted by `...`). ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - CustomizeDiff: CustomizeDiffFunc, - ... + return &schema.Resource{ + CustomizeDiff: CustomizeDiffFunc, + ... ``` ## Framework @@ -62,7 +62,7 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). -**SDKv2** +### SDKv2 In SDKv2, the `CustomizeDiff` field on the `schema.Resource` struct refers to a function or set of functions that implement plan modification. @@ -72,16 +72,16 @@ synchronized (i.e., ensure that they contain the same value) with SDKv2. ```go func resourcePassword() *schema.Resource { - ... - customizeDiffFuncs = append(customizeDiffFuncs, planSyncIfChange("number", "numeric")) - customizeDiffFuncs = append(customizeDiffFuncs, planSyncIfChange("numeric", "number")) - - return &schema.Resource{ - ... - CustomizeDiff: customdiff.All( - customizeDiffFuncs..., - ), - } + ... + customizeDiffFuncs = append(customizeDiffFuncs, planSyncIfChange("number", "numeric")) + customizeDiffFuncs = append(customizeDiffFuncs, planSyncIfChange("numeric", "number")) + + return &schema.Resource{ + ... + CustomizeDiff: customdiff.All( + customizeDiffFuncs..., + ), + } } ``` @@ -89,103 +89,103 @@ The following example shows the implementation of the `planSyncIfChange` functio ```go func planSyncIfChange(key, keyToSync string) func(context.Context, *schema.ResourceDiff, interface{}) error { - return customdiff.IfValueChange( - key, - func(ctx context.Context, oldValue, newValue, meta interface{}) bool { - return oldValue != newValue - }, - func(_ context.Context, d *schema.ResourceDiff, _ interface{}) error { - return d.SetNew(keyToSync, d.Get(key)) - }, - ) + return customdiff.IfValueChange( + key, + func(ctx context.Context, oldValue, newValue, meta interface{}) bool { + return oldValue != newValue + }, + func(_ context.Context, d *schema.ResourceDiff, _ interface{}) error { + return d.SetNew(keyToSync, d.Get(key)) + }, + ) } ``` -**Framework** +### Framework Many existing `CustomizeDiff` implementations would be better suited to migration to attribute plan modifiers in the Framework. This code shows the implementation using attribute plan modifiers with the Framework. ```go func passwordSchemaV2() tfsdk.Schema { - return tfsdk.Schema{ - ... - Attributes: map[string]tfsdk.Attribute{ - ... - "number": { - ... - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.NumberNumericAttributePlanModifier(), - ... - }, - }, - - "numeric": { - ... - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.NumberNumericAttributePlanModifier(), - ... - }, - }, + return tfsdk.Schema{ + ... + Attributes: map[string]tfsdk.Attribute{ + ... + "number": { + ... + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.NumberNumericAttributePlanModifier(), + ... + }, + }, + + "numeric": { + ... + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.NumberNumericAttributePlanModifier(), + ... + }, + }, ``` The following shows an implementation of `NumberNumericAttributePlanModifier` in the Framework. ```go func NumberNumericAttributePlanModifier() tfsdk.AttributePlanModifier { - return &numberNumericAttributePlanModifier{} + return &numberNumericAttributePlanModifier{} } type numberNumericAttributePlanModifier struct { } func (d *numberNumericAttributePlanModifier) Description(ctx context.Context) string { - return "Ensures that number and numeric attributes are kept synchronised." + return "Ensures that number and numeric attributes are kept synchronised." } func (d *numberNumericAttributePlanModifier) MarkdownDescription(ctx context.Context) string { - return d.Description(ctx) + return d.Description(ctx) } func (d *numberNumericAttributePlanModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) { - numberConfig := types.Bool{} - diags := req.Config.GetAttribute(ctx, path.Root("number"), &numberConfig) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - numericConfig := types.Bool{} - req.Config.GetAttribute(ctx, path.Root("numeric"), &numericConfig) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - if !numberConfig.Null && !numericConfig.Null { - resp.Diagnostics.AddError( - "Number numeric attribute plan modifier failed", - "Cannot specify both number and numeric in config", - ) - return - } - - // Default to true for both number and numeric when both are null. - if numberConfig.Null && numericConfig.Null { - resp.AttributePlan = types.Bool{Value: true} - return - } - - // Default to using value for numeric if number is null - if numberConfig.Null && !numericConfig.Null { - resp.AttributePlan = numericConfig - return - } - - // Default to using value for number if numeric is null - if !numberConfig.Null && numericConfig.Null { - resp.AttributePlan = numberConfig - return - } + numberConfig := types.Bool{} + diags := req.Config.GetAttribute(ctx, path.Root("number"), &numberConfig) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + numericConfig := types.Bool{} + req.Config.GetAttribute(ctx, path.Root("numeric"), &numericConfig) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + if !numberConfig.Null && !numericConfig.Null { + resp.Diagnostics.AddError( + "Number numeric attribute plan modifier failed", + "Cannot specify both number and numeric in config", + ) + return + } + + // Default to true for both number and numeric when both are null. + if numberConfig.Null && numericConfig.Null { + resp.AttributePlan = types.Bool{Value: true} + return + } + + // Default to using value for numeric if number is null + if numberConfig.Null && !numericConfig.Null { + resp.AttributePlan = numericConfig + return + } + + // Default to using value for number if numeric is null + if !numberConfig.Null && numericConfig.Null { + resp.AttributePlan = numberConfig + return + } } ``` diff --git a/website/docs/plugin/framework/migrating/resources/schema.mdx b/website/docs/plugin/framework/migrating/resources/schema.mdx index be3d760c1..f35303fb8 100644 --- a/website/docs/plugin/framework/migrating/resources/schema.mdx +++ b/website/docs/plugin/framework/migrating/resources/schema.mdx @@ -28,12 +28,12 @@ The following code shows a basic implementation of resource schema with SDKv2. F ```go func New() *schema.Provider { - return &schema.Provider{ - ResourcesMap: map[string]*schema.Resource - }{ - "resource_example": resourceExample(), - } - ... + return &schema.Provider{ + ResourcesMap: map[string]*schema.Resource + }{ + "resource_example": resourceExample(), + } + ... }, ``` @@ -41,29 +41,29 @@ SDKv2 defines the `schema.Resource` struct as follows. ```go schema.Resource{ - Schema map[string]*Schema - SchemaVersion int - MigrateState StateMigrateFunc - StateUpgraders []StateUpgrader - Create CreateFunc - Read ReadFunc - Update UpdateFunc - Delete DeleteFunc - Exists ExistsFunc - CreateContext CreateContextFunc - ReadContext ReadContextFunc - UpdateContext UpdateContextFunc - DeleteContext DeleteContextFunc - CreateWithoutTimeout CreateContextFunc - ReadWithoutTimeout ReadContextFunc - UpdateWithoutTimeout UpdateContextFunc - DeleteWithoutTimeout DeleteContextFunc - CustomizeDiff CustomizeDiffFunc - Importer *ResourceImporter - DeprecationMessage string - Timeouts *ResourceTimeout - Description string - UseJSONNumber bool + Schema map[string]*Schema + SchemaVersion int + MigrateState StateMigrateFunc + StateUpgraders []StateUpgrader + Create CreateFunc + Read ReadFunc + Update UpdateFunc + Delete DeleteFunc + Exists ExistsFunc + CreateContext CreateContextFunc + ReadContext ReadContextFunc + UpdateContext UpdateContextFunc + DeleteContext DeleteContextFunc + CreateWithoutTimeout CreateContextFunc + ReadWithoutTimeout ReadContextFunc + UpdateWithoutTimeout UpdateContextFunc + DeleteWithoutTimeout DeleteContextFunc + CustomizeDiff CustomizeDiffFunc + Importer *ResourceImporter + DeprecationMessage string + Timeouts *ResourceTimeout + Description string + UseJSONNumber bool } ``` @@ -99,11 +99,11 @@ Framework. type resourceTypeExample struct{} func (r resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{}, nil + return tfsdk.Schema{}, nil } func (r resourceTypeExample) NewResource(_ context.Context, p provider.Provider) (resource.Resource, diag.Diagnostics) { - return &resourceExample{}, nil + return &resourceExample{}, nil } ``` @@ -112,12 +112,12 @@ The `tfsdk.Schema` is used for defining the schema for providers, resources and ```go type Schema struct { - Attributes map[string]Attribute - Blocks map[string]Block - Version int64 - DeprecationMessage string - Description string - MarkdownDescription string + Attributes map[string]Attribute + Blocks map[string]Block + Version int64 + DeprecationMessage string + Description string + MarkdownDescription string } ``` @@ -145,7 +145,7 @@ clone the `terraform-provider-tls` repository and compare the `resource_private_ [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/resource_private_key.go) with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/resource_private_key.go). -**SDKv2** +### SDKv2 In SDKv2, the `ResourcesMap` field on the `schema.Provider` struct holds a `map[string]*schemaResource`. A typical pattern is to implement a function that returns `schema.Resource`. @@ -154,36 +154,36 @@ The following example from the `provider.go` file defines a `tls_private_key` re ```go func New() (*schema.Provider, error) { - return &schema.Provider { - ResourcesMap: map[string]*schema.Resource { - "tls_private_key": resourcePrivateKey(), - ... + return &schema.Provider { + ResourcesMap: map[string]*schema.Resource { + "tls_private_key": resourcePrivateKey(), + ... ``` The following example from the `resource_private_key.go` file defines the resource schema. ```go func resourcePrivateKey() *schema.Resource { - return &schema.Resource{ - CreateContext: createResourcePrivateKey, - DeleteContext: deleteResourcePrivateKey, - ReadContext: readResourcePrivateKey, - - Description: "Creates a PEM ...", - - Schema: map[string]*schema.Schema{ - "algorithm": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(SupportedAlgorithmsStr(), false)), - Description: "Name of the algorithm to use when generating the private key. " + - "Currently-supported values are `RSA`, `ECDSA` and `ED25519`.", - }, - ... + return &schema.Resource{ + CreateContext: createResourcePrivateKey, + DeleteContext: deleteResourcePrivateKey, + ReadContext: readResourcePrivateKey, + + Description: "Creates a PEM ...", + + Schema: map[string]*schema.Schema{ + "algorithm": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(SupportedAlgorithmsStr(), false)), + Description: "Name of the algorithm to use when generating the private key. " + + "Currently-supported values are `RSA`, `ECDSA` and `ED25519`.", + }, + ... ``` -**Framework** +### Framework The following shows the same section of provider code after the migration. @@ -191,8 +191,8 @@ This code defines the `tls_private_key` resource by mapping the resource name to ```go func (p *provider) GetResources(_ context.Context) (map[string]provider.ResourceType, diag.Diagnostics) { - return map[string]provider.ResourceType{ - "tls_private_key": &privateKeyResourceType{}, + return map[string]provider.ResourceType{ + "tls_private_key": &privateKeyResourceType{}, ... ``` @@ -200,25 +200,25 @@ This code defines the `GetSchema` and `NewResource` functions for the `privateKe ```go func (rt *privateKeyResourceType) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Version: 1, - Attributes: map[string]tfsdk.Attribute{ - // Required attributes - "algorithm": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, - Validators: []tfsdk.AttributeValidator{ - attribute_validation.OneOf(supportedAlgorithmsAttrValue()...), - }, - Description: "Name of the algorithm to use when generating the private key. " + - fmt.Sprintf("Currently-supported values are: `%s`. ", strings.Join(supportedAlgorithmsStr(), "`, `")), - }, - ... + return tfsdk.Schema{ + Version: 1, + Attributes: map[string]tfsdk.Attribute{ + // Required attributes + "algorithm": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + Validators: []tfsdk.AttributeValidator{ + attribute_validation.OneOf(supportedAlgorithmsAttrValue()...), + }, + Description: "Name of the algorithm to use when generating the private key. " + + fmt.Sprintf("Currently-supported values are: `%s`. ", strings.Join(supportedAlgorithmsStr(), "`, `")), + }, + ... func (rt *privateKeyResourceType) NewResource(_ context.Context, _ provider.Provider) (resource.Resource, diag.Diagnostics) { - return &privateKeyResource{}, nil + return &privateKeyResource{}, nil } ``` diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx index 53ebf611f..526a57dd1 100644 --- a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -23,14 +23,14 @@ The following code shows a basic implementation of the `stateUpgraders` field in ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - StateUpgraders: []schema.StateUpgrader{ - { - Version: int, - Type: cty.Type, - Upgrade: StateUpgradeFunc, - }, - ... + return &schema.Resource{ + StateUpgraders: []schema.StateUpgrader{ + { + Version: int, + Type: cty.Type, + Upgrade: StateUpgradeFunc, + }, + ... ``` ## Framework @@ -43,12 +43,12 @@ function is omitted for clarity (denoted by `...`). ```go func (r *resourceExample) UpgradeState(context.Context) map[int64]resource.StateUpgrader { - return map[int64]resource.StateUpgrader{ - 0: { - PriorSchema: *tfsdk.Schema, - StateUpgrader: func(context.Context, UpgradeStateRequest, *UpgradeStateResponse), - }, - ... + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: *tfsdk.Schema, + StateUpgrader: func(context.Context, UpgradeStateRequest, *UpgradeStateResponse), + }, + ... ``` The `UpgradeState` function returns a map from state versions to structs that implement state upgrade from the given @@ -74,7 +74,7 @@ For clarity, some parts of the migration are omitted (denoted by `...`). For a c [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). -**SDKv2** +### SDKv2 In SDKv2 the `schema.Resource` struct has a `StateUpgraders` field that holds `[]schema.StateUpgrader` struct(s). @@ -83,49 +83,49 @@ resource with SDKv2. ```go func resourcePassword() *schema.Resource { - return &schema.Resource{ - Schema: passwordSchemaV2(), - SchemaVersion: 2, - StateUpgraders: []schema.StateUpgrader{ - { - Version: 0, - Type: resourcePasswordV0().CoreConfigSchema().ImpliedType(), - Upgrade: resourcePasswordStateUpgradeV0, - }, - { - Version: 1, - Type: resourcePasswordV1().CoreConfigSchema().ImpliedType(), - Upgrade: resourcePasswordStringStateUpgradeV1, - }, - }, - ... + return &schema.Resource{ + Schema: passwordSchemaV2(), + SchemaVersion: 2, + StateUpgraders: []schema.StateUpgrader{ + { + Version: 0, + Type: resourcePasswordV0().CoreConfigSchema().ImpliedType(), + Upgrade: resourcePasswordStateUpgradeV0, + }, + { + Version: 1, + Type: resourcePasswordV1().CoreConfigSchema().ImpliedType(), + Upgrade: resourcePasswordStringStateUpgradeV1, + }, + }, + ... ``` The following example shows the implementation of the `resourcePasswordStateUpgradeV0` function with SDKv2. ```go func resourcePasswordStateUpgradeV0(_ context.Context, rawState map[string]interface{}, _ interface{}) (map[string]interface{}, error) { - if rawState == nil { - return nil, fmt.Errorf("resource password state upgrade failed, state is nil") - } + if rawState == nil { + return nil, fmt.Errorf("resource password state upgrade failed, state is nil") + } - result, ok := rawState["result"].(string) - if !ok { - return nil, fmt.Errorf("resource password state upgrade failed, result is not a string: %T", rawState["result"]) - } + result, ok := rawState["result"].(string) + if !ok { + return nil, fmt.Errorf("resource password state upgrade failed, result is not a string: %T", rawState["result"]) + } - hash, err := generateHash(result) - if err != nil { - return nil, fmt.Errorf("resource password state upgrade failed, generate hash error: %w", err) - } + hash, err := generateHash(result) + if err != nil { + return nil, fmt.Errorf("resource password state upgrade failed, generate hash error: %w", err) + } - rawState["bcrypt_hash"] = hash + rawState["bcrypt_hash"] = hash - return rawState, nil + return rawState, nil } ``` -**Framework** +### Framework The following shows the same section of provider code after the migration. @@ -135,19 +135,19 @@ This code implements the `ResourceWithUpgradeState` interface on the `passwordRe ```go func (r *passwordResource) UpgradeState(context.Context) map[int64]resource.StateUpgrader { - schemaV0 := passwordSchemaV0() - schemaV1 := passwordSchemaV1() - - return map[int64]resource.StateUpgrader{ - 0: { - PriorSchema: &schemaV0, - StateUpgrader: upgradePasswordStateV0toV2, - }, - 1: { - PriorSchema: &schemaV1, - StateUpgrader: upgradePasswordStateV1toV2, - }, - } + schemaV0 := passwordSchemaV0() + schemaV1 := passwordSchemaV1() + + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: &schemaV0, + StateUpgrader: upgradePasswordStateV0toV2, + }, + 1: { + PriorSchema: &schemaV1, + StateUpgrader: upgradePasswordStateV1toV2, + }, + } } ``` @@ -155,53 +155,53 @@ This code implements the `upgradePasswordStateV0toV2` state upgrade function. ```go func upgradePasswordStateV0toV2(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { - type modelV0 struct { - ID types.String `tfsdk:"id"` - Keepers types.Map `tfsdk:"keepers"` - Length types.Int64 `tfsdk:"length"` - Special types.Bool `tfsdk:"special"` - Upper types.Bool `tfsdk:"upper"` - Lower types.Bool `tfsdk:"lower"` - Number types.Bool `tfsdk:"number"` - MinNumeric types.Int64 `tfsdk:"min_numeric"` - MinUpper types.Int64 `tfsdk:"min_upper"` - MinLower types.Int64 `tfsdk:"min_lower"` - MinSpecial types.Int64 `tfsdk:"min_special"` - OverrideSpecial types.String `tfsdk:"override_special"` - Result types.String `tfsdk:"result"` - } - - var passwordDataV0 modelV0 - - resp.Diagnostics.Append(req.State.Get(ctx, &passwordDataV0)...) - if resp.Diagnostics.HasError() { - return - } - - passwordDataV2 := passwordModelV2{ - Keepers: passwordDataV0.Keepers, - Length: passwordDataV0.Length, - Special: passwordDataV0.Special, - Upper: passwordDataV0.Upper, - Lower: passwordDataV0.Lower, - Numeric: passwordDataV0.Number, - MinNumeric: passwordDataV0.MinNumeric, - MinLower: passwordDataV0.MinLower, - MinSpecial: passwordDataV0.MinSpecial, - OverrideSpecial: passwordDataV0.OverrideSpecial, - Result: passwordDataV0.Result, - ID: passwordDataV0.ID, - } - - hash, err := generateHash(passwordDataV2.Result.Value) - if err != nil { - resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) - return - } - - passwordDataV2.BcryptHash.Value = hash - - diags := resp.State.Set(ctx, passwordDataV2) - resp.Diagnostics.Append(diags...) + type modelV0 struct { + ID types.String `tfsdk:"id"` + Keepers types.Map `tfsdk:"keepers"` + Length types.Int64 `tfsdk:"length"` + Special types.Bool `tfsdk:"special"` + Upper types.Bool `tfsdk:"upper"` + Lower types.Bool `tfsdk:"lower"` + Number types.Bool `tfsdk:"number"` + MinNumeric types.Int64 `tfsdk:"min_numeric"` + MinUpper types.Int64 `tfsdk:"min_upper"` + MinLower types.Int64 `tfsdk:"min_lower"` + MinSpecial types.Int64 `tfsdk:"min_special"` + OverrideSpecial types.String `tfsdk:"override_special"` + Result types.String `tfsdk:"result"` + } + + var passwordDataV0 modelV0 + + resp.Diagnostics.Append(req.State.Get(ctx, &passwordDataV0)...) + if resp.Diagnostics.HasError() { + return + } + + passwordDataV2 := passwordModelV2{ + Keepers: passwordDataV0.Keepers, + Length: passwordDataV0.Length, + Special: passwordDataV0.Special, + Upper: passwordDataV0.Upper, + Lower: passwordDataV0.Lower, + Numeric: passwordDataV0.Number, + MinNumeric: passwordDataV0.MinNumeric, + MinLower: passwordDataV0.MinLower, + MinSpecial: passwordDataV0.MinSpecial, + OverrideSpecial: passwordDataV0.OverrideSpecial, + Result: passwordDataV0.Result, + ID: passwordDataV0.ID, + } + + hash, err := generateHash(passwordDataV2.Result.Value) + if err != nil { + resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) + return + } + + passwordDataV2.BcryptHash.Value = hash + + diags := resp.State.Set(ctx, passwordDataV2) + resp.Diagnostics.Append(diags...) } ``` diff --git a/website/docs/plugin/framework/migrating/schema.mdx b/website/docs/plugin/framework/migrating/schema.mdx index 11ca5cdc7..5f2badf99 100644 --- a/website/docs/plugin/framework/migrating/schema.mdx +++ b/website/docs/plugin/framework/migrating/schema.mdx @@ -29,28 +29,28 @@ and data sources with SDKv2. ```go func New() *schema.Provider { - return &schema.Provider{ - Schema: map[string]*schema.Schema{}, + return &schema.Provider{ + Schema: map[string]*schema.Schema{}, ... - } + } } ``` ```go func resourceExample() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{}, - ... - } + return &schema.Resource{ + Schema: map[string]*schema.Schema{}, + ... + } } ``` ```go func dataSourceExample() *schema.Resource { - return &schema.Resource{ + return &schema.Resource{ Schema: map[string]*schema.Schema{}, ... - } + } } ``` @@ -58,30 +58,30 @@ SDKv2 defines the `schema.Schema` struct as follows. ```go type Schema struct { - Type ValueType - ConfigMode SchemaConfigMode - Required bool - Optional bool - Computed bool - ForceNew bool - DiffSuppressFunc SchemaDiffSuppressFunc - DiffSuppressOnRefresh bool - Default interface{} - DefaultFunc SchemaDefaultFunc - Description string - StateFunc SchemaStateFunc - Elem interface{} - MaxItems int - MinItems int - Set SchemaSetFunc - ConflictsWith []string - ExactlyOneOf []string - AtLeastOneOf []string - RequiredWith []string - Deprecated string - ValidateFunc SchemaValidateFunc - ValidateDiagFunc SchemaValidateDiagFunc - Sensitive bool + Type ValueType + ConfigMode SchemaConfigMode + Required bool + Optional bool + Computed bool + ForceNew bool + DiffSuppressFunc SchemaDiffSuppressFunc + DiffSuppressOnRefresh bool + Default interface{} + DefaultFunc SchemaDefaultFunc + Description string + StateFunc SchemaStateFunc + Elem interface{} + MaxItems int + MinItems int + Set SchemaSetFunc + ConflictsWith []string + ExactlyOneOf []string + AtLeastOneOf []string + RequiredWith []string + Deprecated string + ValidateFunc SchemaValidateFunc + ValidateDiagFunc SchemaValidateDiagFunc + Sensitive bool } ``` @@ -100,13 +100,13 @@ func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostic ```go func (r resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{}, nil + return tfsdk.Schema{}, nil } ``` ```go func (r dataSourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{}, nil + return tfsdk.Schema{}, nil } ``` @@ -114,12 +114,12 @@ The Framework defines the `schema.Schema` struct as follows. ```go type Schema struct { - Attributes map[string]Attribute - Blocks map[string]Block - Version int64 - DeprecationMessage string - Description string - MarkdownDescription string + Attributes map[string]Attribute + Blocks map[string]Block + Version int64 + DeprecationMessage string + Description string + MarkdownDescription string } ``` @@ -127,6 +127,7 @@ You use the `Attributes` field to define attributes for your provider, resources `Blocks` field to define named blocks. ## Migration Notes + Remember the following differences between SDKv2 and the Framework when completing the migration. - SDKv2 uses `schema.Schema` structs to define the provider, resources, and data sources. The Framework uses @@ -139,4 +140,3 @@ provider and each resource and data type have a `GetSchema` function that return and data source. - When you populate the `Version` field in `tfsdk.Schema` for a resource in the Framework, copy the `Version` field in `schema.Schema` from the SDKv2 version of that resource. - diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index 937c7ebf4..0351d882b 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -42,38 +42,39 @@ repository. The second step also uses `PlanOnly` to verify that a no-op plan is func TestDataSource_UpgradeFromVersion2_2_0(t *testing.T) { ... - resource.Test(t, resource.TestCase{ - Steps: []resource.TestStep{ - { - ExternalProviders: map[string]resource.ExternalProvider{ - "http": { - VersionConstraint: "2.2.0", - Source: "hashicorp/http", - }, - }, - Config: fmt.Sprintf(` - data "http" "http_test" { - url = "%s/200" - }`, testHttpMock.server.URL), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), - ... - ), - }, - { - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: fmt.Sprintf(` - data "http" "http_test" { - url = "%s/200" - }`, testHttpMock.server.URL), - PlanOnly: true, - }, - }, - }) + resource.Test(t, resource.TestCase{ + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "http": { + VersionConstraint: "2.2.0", + Source: "hashicorp/http", + }, + }, + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), + ... + ), + }, + { + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Config: fmt.Sprintf(` + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), + PlanOnly: true, + }, + }, + }) } ``` ## Provider Factories + Existing tests should require minimal updates when migrating from SDKv2 to the Framework. The only critical change relates to the provider factories that create the provider during the test case. Refer to [Acceptance Tests - Specify Providers](/plugin/framework/acctests#specify-providers) in the Framework documentation for details. @@ -89,10 +90,11 @@ The following example shows a test written in SDKv2. ```go resource.UnitTest(t, resource.TestCase{ - ProviderFactories: testProviders(), + ProviderFactories: testProviders(), ``` ### Framework + In the Framework, use either the `ProtoV5ProviderFactories` or `ProtoV6ProviderFactories` field on the `resource.TestCase` struct to obtain the `provider.Provider`, depending on the [Terraform Plugin Protocol](/plugin/how-terraform-works#terraform-plugin-protocol) version your provider is using. @@ -101,10 +103,11 @@ The following example shows how you can write a test in the Framework for a Prov ```go resource.UnitTest(t, resource.TestCase{ - ProtoV6ProviderFactories: protoV6ProviderFactories(), + ProtoV6ProviderFactories: protoV6ProviderFactories(), ``` ### Example + The following examples show how to migrate portions of the [http](https://github.com/hashicorp/terraform-provider-http) provider. @@ -121,12 +124,12 @@ a test case when using SDKv2. ```go func TestDataSource_http200(t *testing.T) { - ... + ... - resource.UnitTest(t, resource.TestCase{ - ProviderFactories: testProviders(), - ... - }) + resource.UnitTest(t, resource.TestCase{ + ProviderFactories: testProviders(), + ... + }) } ``` @@ -135,13 +138,13 @@ SDKv2. ```go func testProviders() map[string]func() (*schema.Provider, error) { - return map[string]func() (*schema.Provider, error){ - "http": func() (*schema.Provider, error) { return New(), nil }, - } + return map[string]func() (*schema.Provider, error){ + "http": func() (*schema.Provider, error) { return New(), nil }, + } } ``` -**Framework** +### Framework The following code sample is from the `data_source_http_test.go` file and shows how to define provider factories within a test case when using the Framework. @@ -150,10 +153,10 @@ a test case when using the Framework. func TestDataSource_200(t *testing.T) { ... - resource.UnitTest(t, resource.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - ... - }) + resource.UnitTest(t, resource.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + ... + }) } ``` @@ -163,8 +166,8 @@ the Framework. The call to `New` returns an instance of the provider. Refer to ```go func protoV5ProviderFactories() map[string]func() (tfprotov5.ProviderServer, error) { - return map[string]func() (tfprotov5.ProviderServer, error){ - "http": providerserver.NewProtocol5WithError(New()), - } + return map[string]func() (tfprotov5.ProviderServer, error){ + "http": providerserver.NewProtocol5WithError(New()), + } } ``` From 3fe19bcd8dd3a6a09e6ea6f0139e9de77fc99a60 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 30 Aug 2022 11:02:08 +0100 Subject: [PATCH 06/28] Using /* ... */ to indicate code has been truncated and removing statement in the text to same effect. (#459) --- .../attributes-blocks/attribute-schema.mdx | 17 +++++------ .../attributes-blocks/blocks-computed.mdx | 2 +- .../migrating/attributes-blocks/blocks.mdx | 24 ++++++++-------- .../attributes-blocks/default-values.mdx | 5 ++-- .../migrating/attributes-blocks/fields.mdx | 18 ++++++------ .../migrating/attributes-blocks/force-new.mdx | 20 ++++++------- .../migrating/attributes-blocks/types.mdx | 20 ++++++------- .../attributes-blocks/validators-custom.mdx | 2 +- .../validators-predefined.mdx | 2 +- .../framework/migrating/data-sources.mdx | 20 ++++++------- .../plugin/framework/migrating/provider.mdx | 10 +++---- .../framework/migrating/resources/crud.mdx | 17 ++++++----- .../framework/migrating/resources/import.mdx | 14 ++++------ .../migrating/resources/plan-modification.mdx | 28 +++++++++---------- .../framework/migrating/resources/schema.mdx | 17 ++++++----- .../migrating/resources/state-upgrade.mdx | 14 ++++------ .../plugin/framework/migrating/schema.mdx | 6 ++-- .../plugin/framework/migrating/testing.mdx | 16 +++++------ 18 files changed, 118 insertions(+), 134 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx index 675792348..85b9bbd46 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx @@ -16,14 +16,13 @@ In SDKv2, attributes are defined by the `Schema` field in the provider, resource field maps each attribute name (string) to the attribute's `schema.Schema` struct. Both resources and data sources are defined using the `schema.Resource` struct. -The following code shows a basic implementation of attribute schema for a provider in SDKv2. For clarity, the body of -the attribute's schema is omitted (denoted by `...`). +The following code shows a basic implementation of attribute schema for a provider in SDKv2. ```go func ProviderExample() *schema.Provider { return &schema.Provider{ Schema: map[string]*schema.Schema{ - ... + /* ... */ }, ``` @@ -33,8 +32,7 @@ In SDKv2, resource and data source attributes are defined the same way on their func resourceExample() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ - ... - }, + /* ... */ ``` ## Framework @@ -42,15 +40,14 @@ In the Framework, you define attributes by setting the `Attributes` field on you 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. -The following code shows how to define an attribute for a resource with the Framework. For clarity, the body of the -`tfsdk.Attribute` struct is omitted (denoted by `...`). +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": { - ... + /* ... */ ``` The Framework defines the `tfsdk.Attribute` struct as follows. @@ -84,7 +81,7 @@ pages in the Attributes & Blocks section of this migration guide for more detail The following examples show how to migrate portions of the [http](https://github.com/hashicorp/terraform-provider-http) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-http` repository and compare the `data_source.go` file in [v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source.go) and the `data_source_http.go` file in @@ -99,7 +96,7 @@ data source. ```go func dataSource() *schema.Resource { return &schema.Resource{ - ... + /* ... */ Schema: map[string]*schema.Schema{ "url": { Description: "The URL for the request. Supported schemes are `http` and `https`.", diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 0267634b9..a190d12ee 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -61,7 +61,7 @@ map[string]tfsdk.Attribute{ The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-tls` repository and compare the `data_source_certificate.go` file in [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/data_source_certificate.go) with diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx index dbd31f59c..62fcb7f7d 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx @@ -41,7 +41,7 @@ In SDKv2, blocks are defined by an attribute whose type is `TypeList` or `TypeSe ```go func resourceExample() *schema.Resource { return &schema.Resource{ - ... + /* ... */ map[string]*schema.Schema{ "example" = &schema.Schema{ Type: schema.TypeList, @@ -52,7 +52,7 @@ func resourceExample() *schema.Resource { "nested_example": { Type: schema.TypeString, Optional: bool, - ... + /* ... */ ``` ## Framework @@ -64,7 +64,7 @@ In the Framework, you implement nested blocks with the `Blocks` field of your pr ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ - ... + /* ... */ Blocks: map[string]tfsdk.Block{ "example": { NestingMode: tfsdk.BlockNestingModeList, @@ -73,7 +73,7 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia "nested_example": { Type: types.StringType, Optional: bool - ... + /* ... */ ``` @@ -82,7 +82,7 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-tls` repository and compare the `common_cert.go` file in [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/common_cert.go) with the `resource_cert_request.go` file in @@ -97,7 +97,7 @@ The following example from the `common_cert.go` file shows the implementation of map[string]*schema.Schema{ "private_key_pem": &schema.Schema{ Type: schema.TypeString, - ... + /* ... */ "subject" = &schema.Schema{ Type: schema.TypeList, @@ -106,13 +106,13 @@ map[string]*schema.Schema{ Schema: map[string]*schema.Schema{ "organization": { Type: schema.TypeString, - ... + /* ... */ }, "common_name": { Type: schema.TypeString, - ... + /* ... */ }, - ... + /* ... */ ``` ### Framework @@ -125,7 +125,7 @@ tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ "private_key_pem": { Type: types.StringType, - ... + /* ... */ Blocks: map[string]tfsdk.Block{ "subject": { @@ -135,10 +135,10 @@ tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ "organization": { Type: types.StringType, - ... + /* ... */ }, "common_name": { Type: types.StringType, - ... + /* ... */ }, ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx index 5b1f81178..ab3ae4a2d 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx @@ -21,8 +21,7 @@ In SDKv2, default values are defined for a primitive attribute type (i.e., `Type `TypeString`) by the `Default` field on the attribute's schema. Alternatively, the `DefaultFunc` function is used to compute a default value for an attribute. -The following code shows a basic implementation of a default value for a primitive attribute type in SDKv2. For clarity, -the body of the attribute's schema is omitted (denoted by `...`). +The following code shows a basic implementation of a default value for a primitive attribute type in SDKv2. ```go func resourceExample() *schema.Resource { @@ -64,7 +63,7 @@ In the Framework, you must implement an attribute plan modifier to set default v The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-tls` repository and compare the `resource_private_key.go` file in [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/resource_private_key.go) with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/resource_private_key.go). diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx index e4b9b5067..c7e3e8760 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx @@ -19,14 +19,14 @@ In SDKv2, `Required`, `Optional`, `Computed`, and `Sensitive` are boolean fields ```go func resourceExample() *schema.Resource { return &schema.Resource{ - ... + /* ... */ Schema: map[string]*schema.Schema{ "attribute_example": { Required: bool Optional: bool Computed: bool Sensitive: bool - ... + /* ... */ ``` ## Framework @@ -36,14 +36,14 @@ In the Framework, you set the same fields on the `tfsdk.Attribute` struct, with ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ - ... + /* ... */ Attributes: map[string]tfsdk.Attribute{ "attribute_example": { Required: bool Optional: bool Computed: bool Sensitive: bool - ... + /* ... */ ``` ## Example @@ -51,7 +51,7 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia The following examples show how to migrate portions of the [http](https://github.com/hashicorp/terraform-provider-http) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-http` repository and compare the `data_source.go` file in [v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source.go) and the `data_source_http.go` file in @@ -68,9 +68,9 @@ func dataSource() *schema.Resource { Schema: map[string]*schema.Schema{ "url": { Required: true, - ... + /* ... */ }, - ... + /* ... */ ``` ### Framework @@ -84,7 +84,7 @@ func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diag Attributes: map[string]tfsdk.Attribute{ "url": { Required: true, - ... + /* ... */ }, - ... + /* ... */ ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx index b239a5d3a..722e49028 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx @@ -22,11 +22,11 @@ cycle) whenever the attribute's value is changed. ```go func resourceExample() *schema.Resource { return &schema.Resource{ - ... + /* ... */ Schema: map[string]*schema.Schema{ "attribute_example": { ForceNew true - ... + /* ... */ ``` ## Framework @@ -37,12 +37,12 @@ attribute's `tfsdk.Attribute` struct. ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ - ... + /* ... */ Attributes: map[string]tfsdk.Attribute{ "attribute_example": { PlanModifiers: []tfsdk.AttributePlanModifier{ resource.RequiresReplace(), - ... + /* ... */ ``` ## Migration Notes @@ -61,7 +61,7 @@ the plan has already been modified. The following examples show how to migrate portions of the [random](https://github.com/hashicorp/terraform-provider-random) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-random-provider` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). @@ -77,11 +77,11 @@ func resourcePassword() *schema.Resource { Schema: map[string]*schema.Schema{ "keepers": { ForceNew: true, - ... + /* ... */ }, - ... + /* ... */ }, - ... + /* ... */ } } ``` @@ -101,9 +101,9 @@ func (r *passwordResourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Di PlanModifiers: []tfsdk.AttributePlanModifier{ resource.RequiresReplace(), }, - ... + /* ... */ }, - ... + /* ... */ }, }, nil } diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index 480c43fdb..880f49556 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -21,13 +21,13 @@ In SDKv2, attribute types are defined by the `Type` field on the attribute's `sc ```go func resourceExample() *schema.Resource { return &schema.Resource{ - ... + /* ... */ Schema: map[string]*schema.Schema{ "attribute_example": { Type: schema.TypeName, - ... + /* ... */ }, - ... + /* ... */ ``` ## Framework @@ -36,11 +36,11 @@ In the Framework, you set your attribute's type with the `Type` field on your at ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ - ... + /* ... */ Attributes: map[string]tfsdk.Attribute{ "attribute_example": { Type: types.NameType, - ... + /* ... */ }, ``` ## Migration Notes @@ -57,7 +57,7 @@ nested attribute. Nested attributes are also described in more detail on the The following examples show how to migrate portions of the [http](https://github.com/hashicorp/terraform-provider-http) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-http` repository and compare the `data_source.go` file in [v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source.go) and the `data_source_http.go` file in @@ -74,9 +74,9 @@ func dataSource() *schema.Resource { Schema: map[string]*schema.Schema{ "url": { Type: schema.TypeString, - ... + /* ... */ }, - ... + /* ... */ ``` ### Framework @@ -90,7 +90,7 @@ func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diag Attributes: map[string]tfsdk.Attribute{ "url": { Type: types.StringType, - ... + /* ... */ }, - ... + /* ... */ ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx index 9fe025513..1f6b9b2b3 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx @@ -64,7 +64,7 @@ your requirements. The following examples show how to migrate portions of the [random](https://github.com/hashicorp/terraform-provider-random) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx index 0019b9606..7aedafbf6 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -73,7 +73,7 @@ your requirements. The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-tls` repository and compare the `provider.go` file in [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/provider.go) with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/provider.go). diff --git a/website/docs/plugin/framework/migrating/data-sources.mdx b/website/docs/plugin/framework/migrating/data-sources.mdx index e26c08cb2..11096e2e2 100644 --- a/website/docs/plugin/framework/migrating/data-sources.mdx +++ b/website/docs/plugin/framework/migrating/data-sources.mdx @@ -18,14 +18,13 @@ This page explains how to migrate a data source from SDKv2 to the plugin Framewo In SDKv2, data sources are defined by the `DataSourcesMap` field on the `schema.Provider` struct, which maps data source names (strings) to their schema. The `schema.Resource` struct is used for both resources and data sources. -The following example shows a typical implementation. The rest of the `schema.Provider` struct is omitted for clarity -(denoted by `...`). +The following example shows a typical implementation. ```go func New() *schema.Provider { return &schema.Provider{ DataSourcesMap: map[string]*schema.Resource{} - ... + /* ... */ }, ``` @@ -75,11 +74,11 @@ Framework. type dataSourceTypeExample struct{} func (r *dataSourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - ... + /* ... */ } func (r *dataSourceTypeExample) NewDataSource(ctx context.Context, p provider.Provider) (datasource.DataSource, diag.Diagnostics) { - ... + /* ... */ } ``` @@ -99,7 +98,7 @@ Remember the following details when completing the migration from SDKv2 to the F The following examples show how to migrate portions of the [http](https://github.com/hashicorp/terraform-provider-http) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-http` repository and compare the `data_source.go` file in [v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source.go) and the `data_source_http.go` file in @@ -115,7 +114,7 @@ func New() (*schema.Provider, error) { return &schema.Provider { DataSourcesMap: map[string]*schema.Resource { "http": dataSource(), - ... + /* ... */ ``` The following example from the `data_source.go` file shows how the `ReadContext` function and `Schema` are defined for @@ -132,7 +131,7 @@ func dataSource() *schema.Resource { Type: schema.TypeString, Required: true, }, - ... + /* ... */ }, } } @@ -163,7 +162,7 @@ func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diag Type: types.StringType, Required: true, }, - ... + /* ... */ func (d *httpDataSourceType) NewDataSource(context.Context, provider.Provider) (datasource.DataSource, diag.Diagnostics) { return &httpDataSource{}, nil @@ -171,9 +170,8 @@ func (d *httpDataSourceType) NewDataSource(context.Context, provider.Provider) ( ``` This code from the `data_source_http.go` file defines the `Read` function for the `http` data source with the Framework. -The body of the function is omitted from this example for clarity, denoted by (`...`). ```go func (d *httpDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - ... + /* ... */ ``` diff --git a/website/docs/plugin/framework/migrating/provider.mdx b/website/docs/plugin/framework/migrating/provider.mdx index 2c57b505b..8d96945a6 100644 --- a/website/docs/plugin/framework/migrating/provider.mdx +++ b/website/docs/plugin/framework/migrating/provider.mdx @@ -117,8 +117,7 @@ returns a pointer to `schema.Provider`. The `ResourcesMap` and `DataSourcesMap` fields each contain a map of strings to functions that each return a pointer to a `schema.Resource` struct. -The following example shows a basic implementation of an SDKv2 provider. For clarity, the fields for -`ProviderMetaSchema` and `TerraformVersion` are omitted from the `schema.Provider` (denoted by `...`). +The following example shows a basic implementation of an SDKv2 provider. ```go func New() *schema.Provider { @@ -201,7 +200,7 @@ maps data source names to types that you define, which satisfy the `provider.Dat The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-tls` repository and compare `provider.go` in [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/provider.go) with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/provider.go). @@ -282,8 +281,7 @@ In SDKv2, you implement a provider Schema by populating the `Schema` field on th field contains a `map[string]*schema.Schema`. Each map entry represents the name of the attribute and pointer to a `schema.Schema` struct that defines that attribute's behavior. -The following example defines the provider schema in the `Schema` field within the `schema.Provider` struct. The rest -of the `schema.Provider` struct has been omitted for clarity (denoted by `...`). +The following example defines the provider schema in the `Schema` field within the `schema.Provider` struct. ```go func New() *schema.Provider { @@ -325,7 +323,7 @@ Framework `tfsdk.Schema` is a struct that defines `Attributes`, which are struct The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-tls` repository and compare `provider.go` in [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/provider.go) with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/provider.go). diff --git a/website/docs/plugin/framework/migrating/resources/crud.mdx b/website/docs/plugin/framework/migrating/resources/crud.mdx index a60c2e1fb..9de7d9512 100644 --- a/website/docs/plugin/framework/migrating/resources/crud.mdx +++ b/website/docs/plugin/framework/migrating/resources/crud.mdx @@ -18,8 +18,7 @@ This page explains how to migrate a resource's CRUD functions from SDKv2 to the In SDKv2, a resource's CRUD functions are defined by populating the relevant fields (e.g., `CreateContext`, `ReadContext`) on the `schema.Resource` struct. -The following code shows a basic implementation of CRUD functions with SDKv2. For clarity, the rest of the -`schema.Resource` struct is omitted (denoted by `...`). +The following code shows a basic implementation of CRUD functions with SDKv2. ```go func resourceExample() *schema.Resource { @@ -28,7 +27,7 @@ func resourceExample() *schema.Resource { ReadContext: read, UpdateContext: update, DeleteContext: delete, - ... + /* ... */ ``` ## Framework @@ -47,19 +46,19 @@ type resourceExample struct { func (r *resourceExample) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - ... + /* ... */ } func (r *resourceExample) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - ... + /* ... */ } func (r *resourceExample) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - ... + /* ... */ } func (r *resourceExample) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - ... + /* ... */ } ``` ## Migration Notes @@ -81,7 +80,7 @@ on `resource.CreateResponse`. The following examples show how to migrate portions of the [random](https://github.com/hashicorp/terraform-provider-random) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). @@ -98,7 +97,7 @@ func resourcePassword() *schema.Resource { CreateContext: createPassword, ReadContext: readNil, DeleteContext: RemoveResourceFromState, - ... + /* ... */ ``` The following example shows the implementation of the `createPassword()` function with SDKv2. The implementations of diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index 51476fc1c..495634cba 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -17,8 +17,7 @@ This page explains how to migrate import functions from SDKv2 to the plugin Fram ## SDKv2 In SDKv2, the `Importer` field on the `schema.Resource` defines how the provider imports resources to Terraform's -state. The following example implements resource import with SDKv2. The rest of the `schema.Resource` struct is omitted -for clarity (denoted by `...`). +state. The following example implements resource import with SDKv2. ```go func resourceExample() *schema.Resource { @@ -26,7 +25,7 @@ func resourceExample() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: StateContextFunc, }, - ... + /* ... */ ``` The `StateContextFunc` is the function called to import a resource into Terraform state. Any operations that @@ -38,12 +37,11 @@ that is passed into the function. The return value is a slice of `schema.Resourc In the Framework, you implement the `ResourceWithImportState` interface on your `resource.Resource` type to allow users to import a given resource. -The following code shows how you define an `ImportState` function with the Framework. The body of the `ImportState` -function is omitted for clarity (denoted by `...`). +The following code shows how you define an `ImportState` function with the Framework. ```go func (r *resourceExample) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - ... + /* ... */ } ``` @@ -65,7 +63,7 @@ satisfies the `tfsdk.ResourceWithImportState` interface. The following examples show how to migrate portions of the [random](https://github.com/hashicorp/terraform-provider-random) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). @@ -83,7 +81,7 @@ func resourcePassword() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: importPasswordFunc, }, - ... + /* ... */ } } ``` diff --git a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx index 608de45d6..cca3ec97e 100644 --- a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx +++ b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx @@ -14,14 +14,13 @@ Framework. ## SDKv2 In SDKv2, plan modification is implemented with the `CustomizeDiff` field on the `schema.Resource` struct. The following -code shows a basic implementation of plan modification with SDKv2. The rest of the `schema.Resource` struct is omitted -for clarity (denoted by `...`). +code shows a basic implementation of plan modification with SDKv2. ```go func resourceExample() *schema.Resource { return &schema.Resource{ CustomizeDiff: CustomizeDiffFunc, - ... + /* ... */ ``` ## Framework @@ -35,12 +34,11 @@ to implement a plan modifier on an attribute. The `ResourceWithModifyPlan` interface requires a `ModifyPlan` function. -The following code shows how you can implement the `ModifyPlan` function on your `resource.Resource` type. The rest of -the `ModifyPlan` function is omitted for clarity (denoted by `...`). +The following code shows how you can implement the `ModifyPlan` function on your `resource.Resource` type. ```go func (r *resourceExample) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - ... + /* ... */ } ``` ## Migration Notes @@ -57,7 +55,7 @@ Framework. The following examples show how to migrate portions of the [random](https://github.com/hashicorp/terraform-provider-random) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). @@ -72,12 +70,12 @@ synchronized (i.e., ensure that they contain the same value) with SDKv2. ```go func resourcePassword() *schema.Resource { - ... + /* ... */ customizeDiffFuncs = append(customizeDiffFuncs, planSyncIfChange("number", "numeric")) customizeDiffFuncs = append(customizeDiffFuncs, planSyncIfChange("numeric", "number")) return &schema.Resource{ - ... + /* ... */ CustomizeDiff: customdiff.All( customizeDiffFuncs..., ), @@ -109,22 +107,22 @@ Framework. This code shows the implementation using attribute plan modifiers wit ```go func passwordSchemaV2() tfsdk.Schema { return tfsdk.Schema{ - ... + /* ... */ Attributes: map[string]tfsdk.Attribute{ - ... + /* ... */ "number": { - ... + /* ... */ PlanModifiers: []tfsdk.AttributePlanModifier{ planmodifiers.NumberNumericAttributePlanModifier(), - ... + /* ... */ }, }, "numeric": { - ... + /* ... */ PlanModifiers: []tfsdk.AttributePlanModifier{ planmodifiers.NumberNumericAttributePlanModifier(), - ... + /* ... */ }, }, ``` diff --git a/website/docs/plugin/framework/migrating/resources/schema.mdx b/website/docs/plugin/framework/migrating/resources/schema.mdx index f35303fb8..bea5ca258 100644 --- a/website/docs/plugin/framework/migrating/resources/schema.mdx +++ b/website/docs/plugin/framework/migrating/resources/schema.mdx @@ -23,8 +23,7 @@ In SDKv2, resources are defined by the `ResourcesMap` field in the `schema.Provi - Fields for functions to implement state upgrade (`StateUpgraders`), import (`Importer`), and customize diff (`CustomizeDiff`) -The following code shows a basic implementation of resource schema with SDKv2. For clarity, the rest of the -`schema.Provider` struct is omitted (denoted by `...`). +The following code shows a basic implementation of resource schema with SDKv2. ```go func New() *schema.Provider { @@ -33,7 +32,7 @@ func New() *schema.Provider { }{ "resource_example": resourceExample(), } - ... + /* ... */ }, ``` @@ -140,7 +139,7 @@ CRUD operations. The following examples show how to migrate portions of the [tls](https://github.com/hashicorp/terraform-provider-tls) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, +For a complete example, clone the `terraform-provider-tls` repository and compare the `resource_private_key.go` file in [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/resource_private_key.go) with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/resource_private_key.go). @@ -157,7 +156,7 @@ func New() (*schema.Provider, error) { return &schema.Provider { ResourcesMap: map[string]*schema.Resource { "tls_private_key": resourcePrivateKey(), - ... + /* ... */ ``` The following example from the `resource_private_key.go` file defines the resource schema. @@ -169,7 +168,7 @@ func resourcePrivateKey() *schema.Resource { DeleteContext: deleteResourcePrivateKey, ReadContext: readResourcePrivateKey, - Description: "Creates a PEM ...", + Description: "Creates a PEM /* ... */", Schema: map[string]*schema.Schema{ "algorithm": { @@ -180,7 +179,7 @@ func resourcePrivateKey() *schema.Resource { Description: "Name of the algorithm to use when generating the private key. " + "Currently-supported values are `RSA`, `ECDSA` and `ED25519`.", }, - ... + /* ... */ ``` ### Framework @@ -193,7 +192,7 @@ This code defines the `tls_private_key` resource by mapping the resource name to func (p *provider) GetResources(_ context.Context) (map[string]provider.ResourceType, diag.Diagnostics) { return map[string]provider.ResourceType{ "tls_private_key": &privateKeyResourceType{}, - ... + /* ... */ ``` This code defines the `GetSchema` and `NewResource` functions for the `privateKeyResourceType`. @@ -216,7 +215,7 @@ func (rt *privateKeyResourceType) GetSchema(_ context.Context) (tfsdk.Schema, di Description: "Name of the algorithm to use when generating the private key. " + fmt.Sprintf("Currently-supported values are: `%s`. ", strings.Join(supportedAlgorithmsStr(), "`, `")), }, - ... + /* ... */ func (rt *privateKeyResourceType) NewResource(_ context.Context, _ provider.Provider) (resource.Resource, diag.Diagnostics) { return &privateKeyResource{}, nil diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx index 526a57dd1..87559bd4f 100644 --- a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -18,8 +18,7 @@ This page explains how to migrate resource `StateUpgraders` in SDKv2 to `Upgrade In SDKv2, state upgraders are defined by populating the `StateUpgraders` field on the `schema.Resource` struct. Refer to [State Migration](/plugin/sdkv2/resources/state-migration) in the SDKv2 documentation for details. -The following code shows a basic implementation of the `stateUpgraders` field in SDKv2. For clarity, the rest of the -`schema.Resource` struct is omitted (denoted by `...`). +The following code shows a basic implementation of the `stateUpgraders` field in SDKv2. ```go func resourceExample() *schema.Resource { @@ -30,7 +29,7 @@ func resourceExample() *schema.Resource { Type: cty.Type, Upgrade: StateUpgradeFunc, }, - ... + /* ... */ ``` ## Framework @@ -38,8 +37,7 @@ func resourceExample() *schema.Resource { In the Framework, you implement the `ResourceWithUpgradeState` interface on your resource to upgrade your resource's state when required. -The following code shows how you define an `UpgradeState` function with the Framework. The body of the `UpgradeState` -function is omitted for clarity (denoted by `...`). +The following code shows how you define an `UpgradeState` function with the Framework. ```go func (r *resourceExample) UpgradeState(context.Context) map[int64]resource.StateUpgrader { @@ -48,7 +46,7 @@ func (r *resourceExample) UpgradeState(context.Context) map[int64]resource.State PriorSchema: *tfsdk.Schema, StateUpgrader: func(context.Context, UpgradeStateRequest, *UpgradeStateResponse), }, - ... + /* ... */ ``` The `UpgradeState` function returns a map from state versions to structs that implement state upgrade from the given @@ -69,7 +67,7 @@ a single step. For example, version 0 => version 2, version 1 => version 2. The following examples show how to migrate portions of the [random](https://github.com/hashicorp/terraform-provider-random) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). For a complete example, clone the +For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). @@ -98,7 +96,7 @@ func resourcePassword() *schema.Resource { Upgrade: resourcePasswordStringStateUpgradeV1, }, }, - ... + /* ... */ ``` The following example shows the implementation of the `resourcePasswordStateUpgradeV0` function with SDKv2. diff --git a/website/docs/plugin/framework/migrating/schema.mdx b/website/docs/plugin/framework/migrating/schema.mdx index 5f2badf99..207dd652e 100644 --- a/website/docs/plugin/framework/migrating/schema.mdx +++ b/website/docs/plugin/framework/migrating/schema.mdx @@ -31,7 +31,7 @@ and data sources with SDKv2. func New() *schema.Provider { return &schema.Provider{ Schema: map[string]*schema.Schema{}, - ... + /* ... */ } } ``` @@ -40,7 +40,7 @@ func New() *schema.Provider { func resourceExample() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{}, - ... + /* ... */ } } ``` @@ -49,7 +49,7 @@ func resourceExample() *schema.Resource { func dataSourceExample() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{}, - ... + /* ... */ } } ``` diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index 0351d882b..b1f172696 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -28,7 +28,7 @@ the Framework. The following example is taken from [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http_test.go) -of the http provider. Some code is omitted for clarity (denoted by `...`). +of the http provider. This example shows how you can use external providers to generate a state file with a previous version of the provider and then verify that there are no planned changes after migrating to the Framework. @@ -40,7 +40,7 @@ repository. The second step also uses `PlanOnly` to verify that a no-op plan is ```go func TestDataSource_UpgradeFromVersion2_2_0(t *testing.T) { - ... + /* ... */ resource.Test(t, resource.TestCase{ Steps: []resource.TestStep{ @@ -57,7 +57,7 @@ func TestDataSource_UpgradeFromVersion2_2_0(t *testing.T) { }`, testHttpMock.server.URL), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), - ... + /* ... */ ), }, { @@ -111,7 +111,7 @@ resource.UnitTest(t, resource.TestCase{ The following examples show how to migrate portions of the [http](https://github.com/hashicorp/terraform-provider-http) provider. -For clarity, some parts of the migration are omitted (denoted by `...`). To review a complete example, clone the +To review a complete example, clone the `terraform-provider-http` repository and compare [v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source_test.go) with [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http_test.go). @@ -124,11 +124,11 @@ a test case when using SDKv2. ```go func TestDataSource_http200(t *testing.T) { - ... + /* ... */ resource.UnitTest(t, resource.TestCase{ ProviderFactories: testProviders(), - ... + /* ... */ }) } ``` @@ -151,11 +151,11 @@ a test case when using the Framework. ```go func TestDataSource_200(t *testing.T) { - ... + /* ... */ resource.UnitTest(t, resource.TestCase{ ProtoV5ProviderFactories: protoV5ProviderFactories(), - ... + /* ... */ }) } ``` From 77793a8625f4b4282a5fccf6a8f9928984538a39 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 30 Aug 2022 11:06:31 +0100 Subject: [PATCH 07/28] Removing text and link as nothing to link to at present (#459) --- .../plugin/framework/migrating/attributes-blocks/blocks.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx index 62fcb7f7d..75edc7a45 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx @@ -7,8 +7,7 @@ description: >- # Blocks Some providers, resources, and data sources include repeatable nested blocks in their attributes. These nested blocks -typically represent separate objects that are related to (or embedded within) the containing object. Refer to -[Schemas - Blocks](/plugin/framework/FIXME) in the Framework documentation for details. +typically represent separate objects that are related to (or embedded within) the containing object. This page explains how to migrate nested blocks that are not computed (i.e., do not set `Computed: true`) from SDKv2 to the Framework. Refer to From 0b5c8c348a383be9ab0a0f9166ce4b018db22d76 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 30 Aug 2022 11:34:27 +0100 Subject: [PATCH 08/28] Adding an example of migrating schema.TypeList to types.ListType (#459) --- .../migrating/attributes-blocks/types.mdx | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index 880f49556..8695eb0ce 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -94,3 +94,49 @@ func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diag }, /* ... */ ``` + +The following examples show how to migrate portions of the +[http](https://github.com/hashicorp/terraform-provider-tls) provider. + +For a complete example, clone the +`terraform-provider-tls` repository and compare the `common_cert.go` file in +[v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/common_cert.go) +and the `resource_cert_request.go` file in +[v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/resource_cert_request.go). + +### SDKv2 + +The following example from the `common_cert.go` file shows the implementation of the type field of the `dns_names` +attribute with SDKv2. + +```go +func resourceCertRequest() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dns_names": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + /* ... */ + }, + /* ... */ +``` + +### Framework + +The following example from the `data_source_http.go` file shows how the type of the `url` attribute for the `http` data +source is defined with the Framework after the migration. + +```go +func (rt *certRequestResourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "dns_names": { + Type:types.ListType{ + ElemType: types.StringType + }, + /* ... */ + }, + /* ... */ +``` From 0b9f120b2e0aa5f0e7d22cf8cadaa202010d7292 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 30 Aug 2022 12:06:45 +0100 Subject: [PATCH 09/28] Adding example of migrating from schema.ImportStatePassthroughContext to resource.ImportStatePassthroughID (#459) --- .../framework/migrating/resources/import.mdx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index 495634cba..969beee09 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -60,6 +60,32 @@ satisfies the `tfsdk.ResourceWithImportState` interface. ## Example +In the simple use case in which `schema.ImportStatePassthroughContext` has been used with SDKv2, migrating simply +involves using `resource.ImportStatePassthroughID`. + +### SDKv2 + +```go +func resourceExample() *schema.Resource { + return &schema.Resource{ + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + /* ... */ + } +} +``` + +### Framework + +```go + + +func (r *resourceExample) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + /* ... */ +``` + The following examples show how to migrate portions of the [random](https://github.com/hashicorp/terraform-provider-random) provider. From e0eb531d67039d726fecc23b1a823a4293d22837 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 30 Aug 2022 12:10:34 +0100 Subject: [PATCH 10/28] Removing extra blank line (#459) --- website/docs/plugin/framework/migrating/testing.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index b1f172696..652749d88 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -116,7 +116,6 @@ To review a complete example, clone the [v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source_test.go) with [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http_test.go). - **SDKv2** The following code sample is from the `data_source_http_test.go` file and shows how to define provider factories within From f5c4be64e85e4a31b99eb8dd415db299e803ee14 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 1 Sep 2022 08:52:05 +0100 Subject: [PATCH 11/28] Reword to clarify reference to practitioner-configurable blocks (#459) --- .../framework/migrating/attributes-blocks/blocks-computed.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index a190d12ee..3bf08b7e0 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -11,9 +11,9 @@ fields with `Computed: true`, which means that the provider code can define the output of terraform apply (e.g., the ID of an EC2 instance). Refer to [Schemas - Blocks](/plugin/framework/FIXME) in the Framework documentation for details. -This page explains how to migrate computed blocks from SDKv2 to the Framework. Refer to +This page explains how to migrate computed-only blocks from SDKv2 to the Framework. Refer to [Blocks](/plugin/framework/migrating/attributes-blocks/blocks) if you are looking for information about migrating blocks -that do not contain computed fields. +that are practitioner configurable. ## SDKv2 From 0e3d07b6e4e2cf4cb9365bcd6481c5685b5ac58a Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 1 Sep 2022 09:03:00 +0100 Subject: [PATCH 12/28] Replacing ellipsis with in-line comment (#459) --- .../migrating/attributes-blocks/blocks-computed.mdx | 4 ++-- .../migrating/attributes-blocks/default-values.mdx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 3bf08b7e0..f4c7607cf 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -30,7 +30,7 @@ map[string]*schema.Schema{ "nested_example": { Type: schema.TypeString, Computed: true, - ... + /* ... */ ``` ## Framework @@ -48,7 +48,7 @@ map[string]tfsdk.Attribute{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_example": types.StringType, - ... + /* ... */ ``` ## Migration Notes diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx index ab3ae4a2d..a56a6517c 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx @@ -26,13 +26,13 @@ The following code shows a basic implementation of a default value for a primiti ```go func resourceExample() *schema.Resource { return &schema.Resource{ - ... + /* ... */ Schema: map[string]*schema.Schema{ "attribute_example": { Default: 2048, - ... + /* ... */ }, - ... + /* ... */ ``` ## Framework From d781d320994b783d6f0d012fd774f9dddbab4e30 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 1 Sep 2022 09:14:18 +0100 Subject: [PATCH 13/28] Adding example of schema for using nested attributes when migrating computed blocks to framework using protocol version 6 (#459) --- .../attributes-blocks/blocks-computed.mdx | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index f4c7607cf..a740254ab 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -38,19 +38,33 @@ map[string]*schema.Schema{ 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`. -When working with protocol version 6, we recommend that you define computed blocks using nested attributes. +```go +map[string]tfsdk.Attribute{ +"example": { + Computed: true, + Type: types.ListType{ + ElemType: 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. ```go map[string]tfsdk.Attribute{ "example": { Computed: true, - Type: types.ListType{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_example": types.StringType, - /* ... */ + Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "nested_example": { + Computed: true, + Type: types.StringType, + /* ... */ ``` + ## Migration Notes - When using protocol version 5 specify `ElemType` as `types.ObjectType` when migrating blocks that are computed from SDKv2 to Framework. From d654477dedd6f299db01bce14afc7b6cb0d5d914 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 1 Sep 2022 09:37:21 +0100 Subject: [PATCH 14/28] Adding schema examples for common type migrations (#459) --- .../migrating/attributes-blocks/types.mdx | 76 +++++++++++++++++-- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index 8695eb0ce..1c53505fd 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -23,11 +23,44 @@ func resourceExample() *schema.Resource { return &schema.Resource{ /* ... */ Schema: map[string]*schema.Schema{ - "attribute_example": { - Type: schema.TypeName, + "bool_example": { + Type: schema.TypeBool, /* ... */ }, - /* ... */ + "float64_example": { + Type: schema.TypeFloat, + /* ... */ + }, + "int64_example": { + Type: schema.TypeInt, + /* ... */ + }, + "list_example": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeBool, + }, + /* ... */ + }, + "map_example": { + Type: schema.TypeMap, + Elem: &schema.Schema{ + Type: schema.TypeFloat, + }, + /* ... */ + }, + "set_example": { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + /* ... */ + }, + "string_example": { + Type: schema.TypeString, + /* ... */ + }, + /* ... */ ``` ## Framework @@ -38,12 +71,39 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia return tfsdk.Schema{ /* ... */ Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { - Type: types.NameType, - /* ... */ - }, + "bool_example": { + Type: types.BoolType, + /* ... */ + }, + "float64_example": { + Type: types.Float64Type, + /* ... */ + }, + "int64_example": { + Type: types.Int64Type, + /* ... */ + }, + "list_example": { + Type: types.ListType{ElemType: types.BoolType}, + /* ... */ + }, + "map_example": { + Type: types.MapType{ElemType: types.Float64Type}, + /* ... */ + }, + "set_example": { + Type: types.SetType{ElemType: types.Int64Type}, + /* ... */ + }, + "string_example": { + Type: types.StringType, + /* ... */ + }, + /* ... */ ``` + ## Migration Notes + Remember the following differences between SDKv2 and the Framework when completing the migration. - In the Framework, an `Attribute` struct has either the `Type` or `Attributes` field defined. The `Type` field is set to @@ -133,7 +193,7 @@ func (rt *certRequestResourceType) GetSchema(context.Context) (tfsdk.Schema, dia return tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ "dns_names": { - Type:types.ListType{ + Type: types.ListType{ ElemType: types.StringType }, /* ... */ From 9314e01150aa68948ad17007428c32b39ea6d51a Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 1 Sep 2022 09:46:50 +0100 Subject: [PATCH 15/28] Apply suggestions from code review Co-authored-by: Brian Flad --- website/docs/plugin/framework/migrating/resources/import.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index 969beee09..72038daee 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -83,7 +83,7 @@ func resourceExample() *schema.Resource { func (r *resourceExample) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) - /* ... */ +} ``` The following examples show how to migrate portions of the From 474c8bbf88f92faf24d1a467da5eac2b4edefb7c Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 1 Sep 2022 10:02:02 +0100 Subject: [PATCH 16/28] Updating links to tagged version of random provider (#459) --- .../migrating/attributes-blocks/force-new.mdx | 4 ++-- .../attributes-blocks/validators-custom.mdx | 2 +- .../framework/migrating/resources/crud.mdx | 2 +- .../framework/migrating/resources/import.mdx | 2 +- .../migrating/resources/plan-modification.mdx | 16 ++++++++-------- .../migrating/resources/state-upgrade.mdx | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx index 722e49028..1e4b532eb 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx @@ -52,7 +52,7 @@ Remember the following differences between SDKv2 and the Framework when completi - In both SDKv2 and Framework, `ForceNew` and `RequiresReplace`, respectively, only trigger a replace if the attribute is not computed. In the Framework, if an attribute which is computed requires that the resource be replaced when it is changed, implement a plan modifier that triggers the replacement. Refer to -[RequiresReplacePlanModifier](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/planmodifiers/attribute.go#L63) +[RequiresReplacePlanModifier](https://github.com/hashicorp/terraform-provider-random/blob/v3.4.1/internal/planmodifiers/attribute.go#L63) for an example, but bear in mind that each implementation requires different logic and you may need to detect whether the plan has already been modified. @@ -64,7 +64,7 @@ The following examples show how to migrate portions of the For a complete example, clone the `terraform-random-provider` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) -with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). +with [v3.4.1](https://github.com/hashicorp/terraform-provider-random/blob/v3.4.1/internal/provider/resource_password.go). ### SDKv2 diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx index 1f6b9b2b3..a520314c6 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx @@ -67,7 +67,7 @@ The following examples show how to migrate portions of the For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) -with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). +with [v3.4.1](https://github.com/hashicorp/terraform-provider-random/blob/v3.4.1/internal/provider/resource_password.go). ### SDKv2 diff --git a/website/docs/plugin/framework/migrating/resources/crud.mdx b/website/docs/plugin/framework/migrating/resources/crud.mdx index 9de7d9512..35b5d6812 100644 --- a/website/docs/plugin/framework/migrating/resources/crud.mdx +++ b/website/docs/plugin/framework/migrating/resources/crud.mdx @@ -83,7 +83,7 @@ The following examples show how to migrate portions of the For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) -with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). +with [v3.4.1](https://github.com/hashicorp/terraform-provider-random/blob/v3.4.1/internal/provider/resource_password.go). ### SDKv2 diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index 72038daee..5b039b545 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -92,7 +92,7 @@ The following examples show how to migrate portions of the For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) -with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). +with [v3.4.1](https://github.com/hashicorp/terraform-provider-random/blob/v3.4.1/internal/provider/resource_password.go). This example also shows one way to handle populating attributes with their default values during import. diff --git a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx index cca3ec97e..15b9955cd 100644 --- a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx +++ b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx @@ -58,7 +58,7 @@ The following examples show how to migrate portions of the For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) -with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). +with [v3.4.1](https://github.com/hashicorp/terraform-provider-random/blob/v3.4.1/internal/provider/resource_password.go). ### SDKv2 @@ -160,13 +160,13 @@ func (d *numberNumericAttributePlanModifier) Modify(ctx context.Context, req tfs return } - if !numberConfig.Null && !numericConfig.Null { - resp.Diagnostics.AddError( - "Number numeric attribute plan modifier failed", - "Cannot specify both number and numeric in config", - ) - return - } + if !numberConfig.Null && !numericConfig.Null && (numberConfig.Value != numericConfig.Value) { + resp.Diagnostics.AddError( + "Number and numeric are both configured with different values", + "Number is deprecated, use numeric instead", + ) + return + } // Default to true for both number and numeric when both are null. if numberConfig.Null && numericConfig.Null { diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx index 87559bd4f..8b646c178 100644 --- a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -70,7 +70,7 @@ The following examples show how to migrate portions of the For a complete example, clone the `terraform-provider-random` repository and compare the `resource_password.go` file in [v3.3.2](https://github.com/hashicorp/terraform-provider-random/blob/v3.3.2/internal/provider/resource_password.go) -with [main](https://github.com/hashicorp/terraform-provider-random/blob/main/internal/provider/resource_password.go). +with [v3.4.1](https://github.com/hashicorp/terraform-provider-random/blob/v3.4.1/internal/provider/resource_password.go). ### SDKv2 From 653c65c49498012e554ea94eb801c27fd40577b9 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 1 Sep 2022 10:30:09 +0100 Subject: [PATCH 17/28] Updating layout for data sources and providers (#459) --- website/data/plugin-framework-nav-data.json | 21 +++++++++++++++---- .../migrating/attributes-blocks/types.mdx | 4 ++-- .../index.mdx} | 0 .../docs/plugin/framework/migrating/index.mdx | 8 +++---- .../{provider.mdx => providers/index.mdx} | 2 +- .../plugin/framework/migrating/testing.mdx | 2 +- 6 files changed, 25 insertions(+), 12 deletions(-) rename website/docs/plugin/framework/migrating/{data-sources.mdx => data-sources/index.mdx} (100%) rename website/docs/plugin/framework/migrating/{provider.mdx => providers/index.mdx} (99%) diff --git a/website/data/plugin-framework-nav-data.json b/website/data/plugin-framework-nav-data.json index dfd144edf..f8b317db4 100644 --- a/website/data/plugin-framework-nav-data.json +++ b/website/data/plugin-framework-nav-data.json @@ -21,7 +21,10 @@ { "title": "Resources", "routes": [ - { "title": "Overview", "path": "resources" }, + { + "title": "Overview", + "path": "resources" + }, { "title": "Import", "path": "resources/import" @@ -96,8 +99,13 @@ "path": "migrating/schema" }, { - "title": "Provider", - "path": "migrating/provider" + "title": "Providers", + "routes": [ + { + "title": "Overview", + "path": "migrating/providers" + } + ] }, { "title": "Resources", @@ -126,7 +134,12 @@ }, { "title": "Data Sources", - "path": "migrating/data-sources" + "routes": [ + { + "title": "Overview", + "path": "migrating/data-sources" + } + ] }, { "title": "Attributes & Blocks", diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index 1c53505fd..04d5fb7ba 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -12,7 +12,7 @@ that contain other attributes are referred to as nested attributes, and are impl [Schemas - Attributes](/plugin/framework/schemas#type) in the Framework documentation for details. This page explains how to migrate a primitive attribute from SDKv2 to the plugin Framework. For an example of -migrating a nested block to a nested attribute, refer to [Provider Schema](/plugin/framework/migrating/provider#example-1) in the Framework documentation. +migrating a nested block to a nested attribute, refer to [Provider Schema](/plugin/framework/migrating/providers#example-1) in the Framework documentation. ## SDKv2 @@ -108,7 +108,7 @@ Remember the following differences between SDKv2 and the Framework when completi - In the Framework, an `Attribute` struct has either the `Type` or `Attributes` field defined. The `Type` field is set to a primitive type such as an integer or string, and you use the `Attributes` field for `NestedAttributes`. Refer to -[Provider Schema](/plugin/framework/migrating/provider#example-1) for an example of using a single +[Provider Schema](/plugin/framework/migrating/providers#example-1) for an example of using a single nested attribute. Nested attributes are also described in more detail on the [Schemas](https://www.terraform.io/plugin/framework/schemas#attributes-1) page in the Framework documentation. diff --git a/website/docs/plugin/framework/migrating/data-sources.mdx b/website/docs/plugin/framework/migrating/data-sources/index.mdx similarity index 100% rename from website/docs/plugin/framework/migrating/data-sources.mdx rename to website/docs/plugin/framework/migrating/data-sources/index.mdx diff --git a/website/docs/plugin/framework/migrating/index.mdx b/website/docs/plugin/framework/migrating/index.mdx index 6b898e35b..a7217fb28 100644 --- a/website/docs/plugin/framework/migrating/index.mdx +++ b/website/docs/plugin/framework/migrating/index.mdx @@ -35,10 +35,10 @@ As you complete the migration, we recommend that you follow Test Driven Developm Take the following steps when you migrate a provider from SDKv2 to the Framework: - Ensure all [tests](/plugin/framework/migrating/testing#testing-migration) pass. -- [Serve the provider](/plugin/framework/migrating/provider#serving-the-provider) via the Framework. - - Implement [muxing](/plugin/framework/migrating/provider#muxing) if you plan to migrate the provider over several point releases. -- Update the [provider definition](/plugin/framework/migrating/provider#provider-definition) to use the Framework. -- Update the [provider schema](/plugin/framework/migrating/provider#provider-schema). +- [Serve the provider](/plugin/framework/migrating/providers#serving-the-provider) via the Framework. + - Implement [muxing](/plugin/framework/migrating/providers#muxing) if you plan to migrate the provider over several point releases. +- Update the [provider definition](/plugin/framework/migrating/providers#provider-definition) to use the Framework. +- Update the [provider schema](/plugin/framework/migrating/providers#provider-schema). - Update each of the provider's resources and data sources. - Update related [tests](/plugin/framework/migrating/testing) to use the Framework, and ensure that the tests fail. - Update the [resource](/plugin/framework/migrating/resources#schema) or [data source](/plugin/framework/migrating/data-sources) schema. diff --git a/website/docs/plugin/framework/migrating/provider.mdx b/website/docs/plugin/framework/migrating/providers/index.mdx similarity index 99% rename from website/docs/plugin/framework/migrating/provider.mdx rename to website/docs/plugin/framework/migrating/providers/index.mdx index 8d96945a6..197c10200 100644 --- a/website/docs/plugin/framework/migrating/provider.mdx +++ b/website/docs/plugin/framework/migrating/providers/index.mdx @@ -110,7 +110,7 @@ type that implements the `provider.Provider` interface, which you must define. ### SDKv2 -The [`ProviderFunc`](/plugin/framework/migrating/provider#serving-the-provider) field on +The [`ProviderFunc`](/plugin/framework/migrating/providers#serving-the-provider) field on `plugin.ServeOpts` requires a pointer to `schema.Provider`. This is typically satisfied by calling a function that returns a pointer to `schema.Provider`. diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index 652749d88..375e7acf8 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -161,7 +161,7 @@ func TestDataSource_200(t *testing.T) { The following code sample is from the `provider_test.go` file and shows how to generate provider factories when using the Framework. The call to `New` returns an instance of the provider. Refer to -[Provider Definition](/plugin/framework/migrating/provider#provider-definition) in this guide for details. +[Provider Definition](/plugin/framework/migrating/providers#provider-definition) in this guide for details. ```go func protoV5ProviderFactories() map[string]func() (tfprotov5.ProviderServer, error) { From a86abfba8e026c795e3bd67e4df99e489e29f725 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 1 Sep 2022 10:36:06 +0100 Subject: [PATCH 18/28] Removing ellipsis and replacing with comment (#459) --- .../attributes-blocks/attribute-schema.mdx | 4 +-- .../attributes-blocks/blocks-computed.mdx | 4 +-- .../attributes-blocks/default-values.mdx | 18 ++++++------- .../attributes-blocks/validators-custom.mdx | 11 ++++---- .../validators-predefined.mdx | 25 ++++++++++--------- .../framework/migrating/providers/index.mdx | 20 +++++++-------- 6 files changed, 42 insertions(+), 40 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx index 85b9bbd46..fbe7ef9af 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx @@ -115,12 +115,12 @@ This code implements the `url` attribute for the `http` data source with the Fra ```go func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ - ... + /* ... */ Attributes: map[string]tfsdk.Attribute{ "url": { Description: "The URL for the request. Supported schemes are `http` and `https`.", Type: types.StringType, Required: true, }, - ... + /* ... */ ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index a740254ab..630c9781d 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -96,7 +96,7 @@ Schema: map[string]*schema.Schema{ "signature_algorithm": { Type: schema.TypeString, Computed: true, - ... + /* ... */ }, ``` @@ -117,5 +117,5 @@ map[string]tfsdk.Attribute{ }, }, Computed: true, - ... + /* ... */ ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx index a56a6517c..7ac65760a 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx @@ -43,12 +43,12 @@ struct. ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ - ... + /* ... */ Attributes: map[string]tfsdk.Attribute{ "attribute_example": { PlanModifiers: []tfsdk.AttributePlanModifier{ defaultValue(types.Bool{Value: true}), - ... + /* ... */ ``` ## Migration Notes @@ -79,9 +79,9 @@ func resourcePrivateKey() *schema.Resource { Schema: map[string]*schema.Schema{ "rsa_bits": { Default: 2048, - ... + /* ... */ }, - ... + /* ... */ ``` ### Framework @@ -97,11 +97,11 @@ func (rt *privateKeyResourceType) GetSchema(_ context.Context) (tfsdk.Schema, di "rsa_bits": { PlanModifiers: []tfsdk.AttributePlanModifier{ attribute_plan_modifier.DefaultValue(types.Int64{Value: 2048}), - ... + /* ... */ }, - ... + /* ... */ }, - ... + /* ... */ }, }, nil } @@ -122,11 +122,11 @@ type defaultValueAttributePlanModifier struct { var _ tfsdk.AttributePlanModifier = (*defaultValueAttributePlanModifier)(nil) func (apm *defaultValueAttributePlanModifier) Description(ctx context.Context) string { - ... + /* ... */ } func (apm *defaultValueAttributePlanModifier) MarkdownDescription(ctx context.Context) string { - ... + /* ... */ } func (apm *defaultValueAttributePlanModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, res *tfsdk.ModifyAttributePlanResponse) { diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx index a520314c6..0d3608e8d 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx @@ -24,11 +24,12 @@ greater than one. ```go func resourceExample() *schema.Resource { return &schema.Resource{ - ... - Schema: map[string]*schema.Schema{ + /* ... */ + Schema: map[string]*schema.Schema + }{ "attribute_example": { ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)), - ... + /* ... */ ``` ## Framework @@ -42,12 +43,12 @@ greater than one. ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ - ... + /* ... */ Attributes: map[string]tfsdk.Attribute{ "attribute_example": { Validators: []tfsdk.AttributeValidator{ int64validator.AtLeast(1), - ... + /* ... */ ``` ## Migration Notes diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx index 7aedafbf6..83242f364 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -23,8 +23,9 @@ In SDKv2, the `ConflictsWith`, `ExactlyOneOf`, `AtLeastOneOf`, and `RequiredWith ```go func resourceExample() *schema.Resource { return &schema.Resource{ - ... - Schema: map[string]*schema.Schema{ + /* ... */ + Schema: map[string]*schema.Schema + }{ "attribute_example": { ConflictsWith: []string ExactlyOneOf: []string @@ -44,12 +45,12 @@ validators do not meet your needs, you must define ```go func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ - ... + /* ... */ Attributes: map[string]tfsdk.Attribute{ "attribute_example": { Validators: []tfsdk.AttributeValidator{ schemavalidator.ConflictsWith(), - ... + /* ... */ ``` ## Migration Notes @@ -94,19 +95,19 @@ func New() (*schema.Provider, error) { Schema: map[string]*schema.Schema{ "url": { ConflictsWith: []string{"proxy.0.from_env"}, - ... + /* ... */ }, "username": { RequiredWith: []string{"proxy.0.url"}, - ... + /* ... */ }, "password": { RequiredWith: []string{"proxy.0.username"}, - ... + /* ... */ }, "from_env": { ConflictsWith: []string{"proxy.0.url", "proxy.0.username", "proxy.0.password"}, - ... + /* ... */ }, }, }, @@ -133,19 +134,19 @@ func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) Validators: []tfsdk.AttributeValidator{ schemavalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), }, - ... + /* ... */ }, "username": { Validators: []tfsdk.AttributeValidator{ schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("url")), }, - ... + /* ... */ }, "password": { Validators: []tfsdk.AttributeValidator{ schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("username")), }, - ... + /* ... */ }, "from_env": { Validators: []tfsdk.AttributeValidator{ @@ -155,7 +156,7 @@ func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) path.MatchRelative().AtParent().AtName("password"), ), }, - ... + /* ... */ }, }, }, diff --git a/website/docs/plugin/framework/migrating/providers/index.mdx b/website/docs/plugin/framework/migrating/providers/index.mdx index 197c10200..be8f0bf0f 100644 --- a/website/docs/plugin/framework/migrating/providers/index.mdx +++ b/website/docs/plugin/framework/migrating/providers/index.mdx @@ -130,7 +130,7 @@ func New() *schema.Provider { DataSourcesMap: map[string]*schema.Resource{ "dataSource_example": dataSourceExample(), }, - ... + /* ... */ } } ``` @@ -214,17 +214,17 @@ func New() (*schema.Provider, error) { return &schema.Provider{ Schema: map[string]*schema.Schema{ "proxy": { - ... + /* ... */ }, }, ConfigureContextFunc: configureProvider, ResourcesMap: map[string]*schema.Resource{ "tls_private_key": resourcePrivateKey(), - ... + /* ... */ }, DataSourcesMap: map[string]*schema.Resource{ "tls_public_key": dataSourcePublicKey(), - ... + /* ... */ }, }, nil } @@ -244,14 +244,14 @@ func New() provider.Provider { func (p *provider) GetResources(_ context.Context) (map[string]provider.ResourceType, diag.Diagnostics) { return map[string]provider.ResourceType{ "tls_private_key": &privateKeyResourceType{}, - ... + /* ... */ }, nil } func (p *provider) GetDataSources(_ context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { return map[string]provider.DataSourceType{ "tls_public_key": &publicKeyDataSourceType{}, - ... + /* ... */ }, nil } @@ -259,14 +259,14 @@ func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) return tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ "proxy": { - ... + /* ... */ }, }, }, nil } func (p *provider) Configure(ctx context.Context, req provider.ConfigureRequest, res *provider.ConfigureResponse) { - ... + /* ... */ } ``` @@ -287,7 +287,7 @@ The following example defines the provider schema in the `Schema` field within t func New() *schema.Provider { return &schema.Provider{ Schema: map[string]*schema.Schema{}, - ... + /* ... */ ``` ### Framework @@ -352,7 +352,7 @@ Schema: map[string]*schema.Schema{ Description: "URL used to connect to the Proxy. " + fmt.Sprintf("Accepted schemes are: `%s`. ", strings.Join(SupportedProxySchemesStr(), "`, `")), }, - ... + /* ... */ ``` **Framework** From 84fd690d8a1739947a3761b7496d3d3ceca0c453 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 1 Sep 2022 11:05:13 +0100 Subject: [PATCH 19/28] Formatting code examples (#459) --- .../migrating/attributes-blocks/types.mdx | 2 +- .../attributes-blocks/validators-predefined.mdx | 4 ++-- .../framework/migrating/data-sources/index.mdx | 4 ++-- .../plugin/framework/migrating/providers/index.mdx | 4 ++-- .../plugin/framework/migrating/resources/crud.mdx | 2 +- .../framework/migrating/resources/import.mdx | 2 +- .../docs/plugin/framework/migrating/testing.mdx | 14 ++++++-------- 7 files changed, 15 insertions(+), 17 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index 04d5fb7ba..8e357021c 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -109,7 +109,7 @@ Remember the following differences between SDKv2 and the Framework when completi - In the Framework, an `Attribute` struct has either the `Type` or `Attributes` field defined. The `Type` field is set to a primitive type such as an integer or string, and you use the `Attributes` field for `NestedAttributes`. Refer to [Provider Schema](/plugin/framework/migrating/providers#example-1) for an example of using a single -nested attribute. Nested attributes are also described in more detail on the +nested attribute. Nested attributes are also described in more detail on the [Schemas](https://www.terraform.io/plugin/framework/schemas#attributes-1) page in the Framework documentation. ## Example diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx index 83242f364..5718e11ab 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -28,10 +28,10 @@ func resourceExample() *schema.Resource { }{ "attribute_example": { ConflictsWith: []string - ExactlyOneOf: []string + ExactlyOneOf: []string AtLeastOneOf: []string RequiredWith: []string -... + /* ... */ ``` ## Framework diff --git a/website/docs/plugin/framework/migrating/data-sources/index.mdx b/website/docs/plugin/framework/migrating/data-sources/index.mdx index 11096e2e2..d019ce936 100644 --- a/website/docs/plugin/framework/migrating/data-sources/index.mdx +++ b/website/docs/plugin/framework/migrating/data-sources/index.mdx @@ -24,7 +24,7 @@ The following example shows a typical implementation. func New() *schema.Provider { return &schema.Provider{ DataSourcesMap: map[string]*schema.Resource{} - /* ... */ + /* ... */ }, ``` @@ -162,7 +162,7 @@ func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diag Type: types.StringType, Required: true, }, - /* ... */ + /* ... */ func (d *httpDataSourceType) NewDataSource(context.Context, provider.Provider) (datasource.DataSource, diag.Diagnostics) { return &httpDataSource{}, nil diff --git a/website/docs/plugin/framework/migrating/providers/index.mdx b/website/docs/plugin/framework/migrating/providers/index.mdx index be8f0bf0f..8740e55f0 100644 --- a/website/docs/plugin/framework/migrating/providers/index.mdx +++ b/website/docs/plugin/framework/migrating/providers/index.mdx @@ -93,7 +93,7 @@ func main() { } err = tf5server.Serve( - "registry.terraform.io/hashicorp/time", + "registry.terraform.io//", muxServer.ProviderServer, serveOpts..., ) @@ -170,7 +170,7 @@ func (p *provider) GetResources(ctx context.Context) (map[string]provider.Resour func (p *provider) GetDataSources(ctx context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { return map[string]provider.DataSourceType{ - "data_source_example": dataSourceTypeExample{}, + "data_source_example": dataSourceTypeExample{}, }, nil } ``` diff --git a/website/docs/plugin/framework/migrating/resources/crud.mdx b/website/docs/plugin/framework/migrating/resources/crud.mdx index 35b5d6812..840b13bc8 100644 --- a/website/docs/plugin/framework/migrating/resources/crud.mdx +++ b/website/docs/plugin/framework/migrating/resources/crud.mdx @@ -96,7 +96,7 @@ func resourcePassword() *schema.Resource { return &schema.Resource{ CreateContext: createPassword, ReadContext: readNil, - DeleteContext: RemoveResourceFromState, + DeleteContext: RemoveResourceFromState, /* ... */ ``` diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index 5b039b545..f435af27a 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -82,7 +82,7 @@ func resourceExample() *schema.Resource { func (r *resourceExample) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } ``` diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index 375e7acf8..43723ca01 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -41,7 +41,6 @@ repository. The second step also uses `PlanOnly` to verify that a no-op plan is ```go func TestDataSource_UpgradeFromVersion2_2_0(t *testing.T) { /* ... */ - resource.Test(t, resource.TestCase{ Steps: []resource.TestStep{ { @@ -52,9 +51,9 @@ func TestDataSource_UpgradeFromVersion2_2_0(t *testing.T) { }, }, Config: fmt.Sprintf(` - data "http" "http_test" { - url = "%s/200" - }`, testHttpMock.server.URL), + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("data.http.http_test", "response_body", "1.0.0"), /* ... */ @@ -63,9 +62,9 @@ func TestDataSource_UpgradeFromVersion2_2_0(t *testing.T) { { ProtoV5ProviderFactories: protoV5ProviderFactories(), Config: fmt.Sprintf(` - data "http" "http_test" { - url = "%s/200" - }`, testHttpMock.server.URL), + data "http" "http_test" { + url = "%s/200" + }`, testHttpMock.server.URL), PlanOnly: true, }, }, @@ -124,7 +123,6 @@ a test case when using SDKv2. ```go func TestDataSource_http200(t *testing.T) { /* ... */ - resource.UnitTest(t, resource.TestCase{ ProviderFactories: testProviders(), /* ... */ From 017d41669b3f16da73a8bc9147ae0f433e19c3e4 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 2 Sep 2022 12:59:29 +0100 Subject: [PATCH 20/28] Updating computed-blocks example to use ListNestedAttributes (#459) --- .../migrating/attributes-blocks/blocks-computed.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 630c9781d..429ed7667 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -51,13 +51,15 @@ map[string]tfsdk.Attribute{ ``` In the Framework, when working with protocol version 6, we recommend that you define computed blocks using nested -attributes. +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 +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.SingleNestedAttributes(map[string]tfsdk.Attribute{ + Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ "nested_example": { Computed: true, Type: types.StringType, From 1ae0d44e5340c413c5b6d54649b59573f6b000e0 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 6 Sep 2022 08:07:46 +0100 Subject: [PATCH 21/28] Restructuring resources and updating state upgrade example (#459) --- website/data/plugin-framework-nav-data.json | 4 +- .../resources/{schema.mdx => index.mdx} | 5 +- .../migrating/resources/state-upgrade.mdx | 99 ++++++++++--------- 3 files changed, 54 insertions(+), 54 deletions(-) rename website/docs/plugin/framework/migrating/resources/{schema.mdx => index.mdx} (98%) diff --git a/website/data/plugin-framework-nav-data.json b/website/data/plugin-framework-nav-data.json index f8b317db4..4bf18686a 100644 --- a/website/data/plugin-framework-nav-data.json +++ b/website/data/plugin-framework-nav-data.json @@ -111,8 +111,8 @@ "title": "Resources", "routes": [ { - "title": "Schema", - "path": "migrating/resources/schema" + "title": "Overview", + "path": "migrating/resources" }, { "title": "CRUD Functions", diff --git a/website/docs/plugin/framework/migrating/resources/schema.mdx b/website/docs/plugin/framework/migrating/resources/index.mdx similarity index 98% rename from website/docs/plugin/framework/migrating/resources/schema.mdx rename to website/docs/plugin/framework/migrating/resources/index.mdx index bea5ca258..0f6f7dee7 100644 --- a/website/docs/plugin/framework/migrating/resources/schema.mdx +++ b/website/docs/plugin/framework/migrating/resources/index.mdx @@ -1,11 +1,10 @@ --- page_title: 'Resources - Schema: Migrating from SDKv2 to the Framework' description: >- - Schemas define resource fields and give Terraform metadata about those fields. - Migrate a resource schema from SDKv2 to the plugin Framework. + Migrate a resource from SDKv2 to the plugin Framework. --- -# Resource Schema +# Resources Resources are an abstraction that allow Terraform to manage infrastructure objects by defining create, read, update, and delete functionality that maps onto API operations. Resource schemas define what fields a resource has, give diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx index 8b646c178..fa1240f63 100644 --- a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -153,53 +153,54 @@ This code implements the `upgradePasswordStateV0toV2` state upgrade function. ```go func upgradePasswordStateV0toV2(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { - type modelV0 struct { - ID types.String `tfsdk:"id"` - Keepers types.Map `tfsdk:"keepers"` - Length types.Int64 `tfsdk:"length"` - Special types.Bool `tfsdk:"special"` - Upper types.Bool `tfsdk:"upper"` - Lower types.Bool `tfsdk:"lower"` - Number types.Bool `tfsdk:"number"` - MinNumeric types.Int64 `tfsdk:"min_numeric"` - MinUpper types.Int64 `tfsdk:"min_upper"` - MinLower types.Int64 `tfsdk:"min_lower"` - MinSpecial types.Int64 `tfsdk:"min_special"` - OverrideSpecial types.String `tfsdk:"override_special"` - Result types.String `tfsdk:"result"` - } - - var passwordDataV0 modelV0 - - resp.Diagnostics.Append(req.State.Get(ctx, &passwordDataV0)...) - if resp.Diagnostics.HasError() { - return - } - - passwordDataV2 := passwordModelV2{ - Keepers: passwordDataV0.Keepers, - Length: passwordDataV0.Length, - Special: passwordDataV0.Special, - Upper: passwordDataV0.Upper, - Lower: passwordDataV0.Lower, - Numeric: passwordDataV0.Number, - MinNumeric: passwordDataV0.MinNumeric, - MinLower: passwordDataV0.MinLower, - MinSpecial: passwordDataV0.MinSpecial, - OverrideSpecial: passwordDataV0.OverrideSpecial, - Result: passwordDataV0.Result, - ID: passwordDataV0.ID, - } - - hash, err := generateHash(passwordDataV2.Result.Value) - if err != nil { - resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) - return - } - - passwordDataV2.BcryptHash.Value = hash - - diags := resp.State.Set(ctx, passwordDataV2) - resp.Diagnostics.Append(diags...) -} + type modelV0 struct { + ID types.String `tfsdk:"id"` + Keepers types.Map `tfsdk:"keepers"` + Length types.Int64 `tfsdk:"length"` + Special types.Bool `tfsdk:"special"` + Upper types.Bool `tfsdk:"upper"` + Lower types.Bool `tfsdk:"lower"` + Number types.Bool `tfsdk:"number"` + MinNumeric types.Int64 `tfsdk:"min_numeric"` + MinUpper types.Int64 `tfsdk:"min_upper"` + MinLower types.Int64 `tfsdk:"min_lower"` + MinSpecial types.Int64 `tfsdk:"min_special"` + OverrideSpecial types.String `tfsdk:"override_special"` + Result types.String `tfsdk:"result"` + } + + var passwordDataV0 modelV0 + + resp.Diagnostics.Append(req.State.Get(ctx, &passwordDataV0)...) + if resp.Diagnostics.HasError() { + return + } + + passwordDataV2 := passwordModelV2{ + Keepers: passwordDataV0.Keepers, + Length: passwordDataV0.Length, + Special: passwordDataV0.Special, + Upper: passwordDataV0.Upper, + Lower: passwordDataV0.Lower, + Number: passwordDataV0.Number, + Numeric: passwordDataV0.Number, + MinNumeric: passwordDataV0.MinNumeric, + MinUpper: passwordDataV0.MinUpper, + MinLower: passwordDataV0.MinLower, + MinSpecial: passwordDataV0.MinSpecial, + OverrideSpecial: passwordDataV0.OverrideSpecial, + Result: passwordDataV0.Result, + ID: passwordDataV0.ID, + } + + hash, err := generateHash(passwordDataV2.Result.Value) + if err != nil { + resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...) + return + } + + passwordDataV2.BcryptHash.Value = hash + + diags := resp.State.Set(ctx, passwordDataV2) + resp.Diagnostics.Append(diags...) ``` From 1afd2430c273958631acd3e188f668b60e3ab98e Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Wed, 7 Sep 2022 07:56:49 +0100 Subject: [PATCH 22/28] Formatting headers (#459) --- .../docs/plugin/framework/migrating/providers/index.mdx | 8 ++++---- website/docs/plugin/framework/migrating/testing.mdx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/website/docs/plugin/framework/migrating/providers/index.mdx b/website/docs/plugin/framework/migrating/providers/index.mdx index 8740e55f0..c54305507 100644 --- a/website/docs/plugin/framework/migrating/providers/index.mdx +++ b/website/docs/plugin/framework/migrating/providers/index.mdx @@ -205,7 +205,7 @@ For a complete example, clone the [v3.4.0](https://github.com/hashicorp/terraform-provider-tls/blob/v3.4.0/internal/provider/provider.go) with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/internal/provider/provider.go). -**SDKv2** +#### SDKv2 The following example shows how to set up a provider schema, configuration, resources, and data sources using SDKv2. @@ -230,7 +230,7 @@ func New() (*schema.Provider, error) { } ``` -**Framework** +#### Framework The following shows the same section of provider code after the migration. @@ -332,7 +332,7 @@ This example also shows how to use a nested block and a nested attribute for the respectively. Refer to the [Blocks and Nested Attributes](/plugin/framework/migrating/attributes-blocks) page in this guide for more details. -**SDKv2** +#### SDKv2 The following example shows how to configure an attribute within a provider schema using SDKv2. @@ -355,7 +355,7 @@ Schema: map[string]*schema.Schema{ /* ... */ ``` -**Framework** +#### Framework The following example shows how to configure provider schema using the Framework. diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index 43723ca01..740cd86f6 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -115,7 +115,7 @@ To review a complete example, clone the [v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source_test.go) with [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http_test.go). -**SDKv2** +#### SDKv2 The following code sample is from the `data_source_http_test.go` file and shows how to define provider factories within a test case when using SDKv2. @@ -141,7 +141,7 @@ func testProviders() map[string]func() (*schema.Provider, error) { } ``` -### Framework +#### Framework The following code sample is from the `data_source_http_test.go` file and shows how to define provider factories within a test case when using the Framework. From 7a07a06e585b28caf8b76d4ae00432d5794ffca8 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Wed, 7 Sep 2022 08:10:56 +0100 Subject: [PATCH 23/28] Apply suggestions from code review Co-authored-by: Brian Flad Co-authored-by: Laura Pacilio <83350965+laurapacilio@users.noreply.github.com> --- website/docs/plugin/framework/migrating/index.mdx | 7 +------ .../plugin/framework/migrating/resources/index.mdx | 8 ++++++-- website/docs/plugin/framework/migrating/schema.mdx | 11 ++++++++++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/website/docs/plugin/framework/migrating/index.mdx b/website/docs/plugin/framework/migrating/index.mdx index a7217fb28..54cec3554 100644 --- a/website/docs/plugin/framework/migrating/index.mdx +++ b/website/docs/plugin/framework/migrating/index.mdx @@ -41,11 +41,6 @@ Take the following steps when you migrate a provider from SDKv2 to the Framework - Update the [provider schema](/plugin/framework/migrating/providers#provider-schema). - Update each of the provider's resources and data sources. - Update related [tests](/plugin/framework/migrating/testing) to use the Framework, and ensure that the tests fail. - - Update the [resource](/plugin/framework/migrating/resources#schema) or [data source](/plugin/framework/migrating/data-sources) schema. - - Update the resource or data source's [CRUD function](/plugin/framework/migrating/resources/crud) definitions. - - Update the resource or data source's [attribute schema](/plugin/framework/migrating/attributes/schema). - - Update the resource's [import function](/plugin/framework/migrating/resources/import), if your provider supports importing the resource. - - Implement [plan modification](/plugin/framework/migrating/resources/plan-modification) if needed. - - Implement [state upgraders](/plugin/framework/migrating/resources/plan-modification) if needed. + - Migrate the [resource](/plugin/framework/migrating/resources) or [data source](/plugin/framework/migrating/data-sources). - Verify that related tests now pass. - If you used muxing, remove the muxing configuration. diff --git a/website/docs/plugin/framework/migrating/resources/index.mdx b/website/docs/plugin/framework/migrating/resources/index.mdx index 0f6f7dee7..987b4cd81 100644 --- a/website/docs/plugin/framework/migrating/resources/index.mdx +++ b/website/docs/plugin/framework/migrating/resources/index.mdx @@ -1,5 +1,5 @@ --- -page_title: 'Resources - Schema: Migrating from SDKv2 to the Framework' +page_title: 'Resources: Migrating from SDKv2 to the Framework' description: >- Migrate a resource from SDKv2 to the plugin Framework. --- @@ -11,7 +11,11 @@ and delete functionality that maps onto API operations. Resource schemas define Terraform metadata about those fields, and define how the resource behaves. Refer to [Resources](/plugin/framework/resources) in the Framework documentation for details. -This page explains how to migrate a resource's schema from SDKv2 to the plugin Framework. +This page explains how to migrate a resource's schema from SDKv2 to the plugin Framework. We also recommend reviewing these additional guides for resources throughout the migration: +- [Create, Read, Update, and Delete functions](/plugin/framework/migrating/resources/crud): The resource defines the logic to manage resources with Terraform. +- [Import](/plugin/framework/migrating/resources/import): The resource defines the logic to add a resource to Terraform's state. +- [Plan modification](/plugin/framework/migrating/resources/plan-modification): The resource customizes the Terraform plan for known values or behaviors outside the practitioner's configuration. +- [State upgrade](/plugin/framework/migrating/resources/state-upgrade): The resource updates Terraform state information in advanced use cases. ## SDKv2 In SDKv2, resources are defined by the `ResourcesMap` field in the `schema.Provider` struct, which maps resource names diff --git a/website/docs/plugin/framework/migrating/schema.mdx b/website/docs/plugin/framework/migrating/schema.mdx index 207dd652e..1b99b14dc 100644 --- a/website/docs/plugin/framework/migrating/schema.mdx +++ b/website/docs/plugin/framework/migrating/schema.mdx @@ -10,7 +10,16 @@ Providers, resources, and data sources all use schema to define their attributes constraints of Terraform configuration blocks and how the provider, resource, or data source behaves. Refer to [Schemas](/plugin/framework/schemas) in the Framework documentation for details. -This page explains the differences between the schema used by SDKv2 and the Framework. +This page explains the differences between the schema used by SDKv2 and the Framework. For further schema-specific documentation, refer to the other migration guides in this section: + +- [Attributes](/plugin/framework/migrating/attribute-blocks/attribute-schema) where the schema defines practitioner or provider data associated with a value and type. +- [Attribute types](/plugin/framework/migrating/attribute-blocks/types) where the schema defines the expected data structure and syntax. +- [Attribute fields](/plugin/framework/migrating/attribute-blocks/fields) where the behaviors of an attribute are defined, such as `Required`, `Optional`, `Computed`, and `Sensitive`. +- [Attribute defaults](/plugin/framework/migrating/attribute-blocks/default-values) where the schema defines a value for an attribute which should be automatically included in a Terraform plan if it is not configured by the practitioner. +- [Attributes without in-place updates](/plugin/framework/migrating/force-new) where the schema defines an attribute that requires resource replacement if the value is updated. +- [Attribute predefined validations](/plugin/framework/migrating/attribute-blocks/validators-predefined) and [custom validations](/plugin/framework/migrating/attribute-blocks/validators-custom) where the schema defines the syntax, constraints, or encoding expectations of a value. +- [Blocks](/plugin/framework/migrating/attribute-blocks/attribute-schema) and [computed blocks](/plugin/framework/migrating/attribute-blocks/blocks-computed) where the schema defines structural configuration sections of data, typically with nested attributes or further blocks. + ## Schema Structs From be4cb9e257034d00b607b3f5c1b217be0f476450 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Wed, 7 Sep 2022 08:59:36 +0100 Subject: [PATCH 24/28] Restructuring top-level Schemas page, adding example for config headers (#459) --- website/data/plugin-framework-nav-data.json | 7 ++++++- .../attributes-blocks/validators-predefined.mdx | 16 ++++++++++++++++ .../framework/migrating/resources/index.mdx | 1 - .../migrating/{schema.mdx => schema/index.mdx} | 0 4 files changed, 22 insertions(+), 2 deletions(-) rename website/docs/plugin/framework/migrating/{schema.mdx => schema/index.mdx} (100%) diff --git a/website/data/plugin-framework-nav-data.json b/website/data/plugin-framework-nav-data.json index 4bf18686a..9a9c61d97 100644 --- a/website/data/plugin-framework-nav-data.json +++ b/website/data/plugin-framework-nav-data.json @@ -96,7 +96,12 @@ }, { "title": "Schema", - "path": "migrating/schema" + "routes": [ + { + "title": "Overview", + "path": "migrating/schema" + } + ] }, { "title": "Providers", diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx index 5718e11ab..35ea0fd67 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -53,6 +53,22 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia /* ... */ ``` +Configuration validators can also be defined for +[providers](https://github.com/hashicorp/terraform-plugin-framework-validators/tree/main/providervalidator), +[resources](https://github.com/hashicorp/terraform-plugin-framework-validators/tree/main/resourcevalidator) and +[data sources](https://github.com/hashicorp/terraform-plugin-framework-validators/tree/main/datasourcevalidator) by +implementing `ProviderWithConfigValidators`, `ResourceWithConfigValidators` and `DataSourceWithConfigValidators` +interfaces, respectively. + +```go +func (r *resourceExample) ConfigValidators(ctx context.Context) []resource.ConfigValidator { + return []resource.ConfigValidator{ + resourcevalidator.Conflicting( + /* ... */ + ), + /* ... */ +``` + ## Migration Notes Remember the following details when migrating from SDKv2 to the Framework. diff --git a/website/docs/plugin/framework/migrating/resources/index.mdx b/website/docs/plugin/framework/migrating/resources/index.mdx index 987b4cd81..553f82155 100644 --- a/website/docs/plugin/framework/migrating/resources/index.mdx +++ b/website/docs/plugin/framework/migrating/resources/index.mdx @@ -203,7 +203,6 @@ This code defines the `GetSchema` and `NewResource` functions for the `privateKe ```go func (rt *privateKeyResourceType) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ - Version: 1, Attributes: map[string]tfsdk.Attribute{ // Required attributes "algorithm": { diff --git a/website/docs/plugin/framework/migrating/schema.mdx b/website/docs/plugin/framework/migrating/schema/index.mdx similarity index 100% rename from website/docs/plugin/framework/migrating/schema.mdx rename to website/docs/plugin/framework/migrating/schema/index.mdx From 64b35fcbe11d0cafabb7b01b00b13fce281f5ee7 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 8 Sep 2022 12:06:42 +0100 Subject: [PATCH 25/28] Apply suggestions from code review Co-authored-by: Robin Norwood Co-authored-by: Laura Pacilio <83350965+laurapacilio@users.noreply.github.com> Co-authored-by: Brian Flad --- .../attributes-blocks/attribute-schema.mdx | 2 +- .../attributes-blocks/blocks-computed.mdx | 5 ++--- .../migrating/attributes-blocks/fields.mdx | 4 ++-- .../migrating/attributes-blocks/force-new.mdx | 2 +- .../migrating/attributes-blocks/types.mdx | 2 +- .../attributes-blocks/validators-custom.mdx | 5 ++--- .../validators-predefined.mdx | 21 +++++++++---------- .../migrating/data-sources/index.mdx | 6 +++--- .../framework/migrating/providers/index.mdx | 21 ++++++++++++------- .../framework/migrating/resources/crud.mdx | 2 +- .../framework/migrating/resources/import.mdx | 11 ++++------ .../migrating/resources/plan-modification.mdx | 2 +- .../framework/migrating/schema/index.mdx | 2 +- .../plugin/framework/migrating/testing.mdx | 6 +++--- 14 files changed, 45 insertions(+), 46 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx index fbe7ef9af..fc27d3436 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx @@ -84,7 +84,7 @@ The following examples show how to migrate portions of the For a complete example, clone the `terraform-provider-http` repository and compare the `data_source.go` file in [v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source.go) -and the `data_source_http.go` file in +with the `data_source_http.go` file in [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http.go). diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 429ed7667..0fc395985 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -8,8 +8,7 @@ description: >- Some providers, resources, and data sources include repeatable nested blocks in their attributes. Some blocks contain fields with `Computed: true`, which means that the provider code can define the value or that it could come from the -output of terraform apply (e.g., the ID of an EC2 instance). Refer to [Schemas - Blocks](/plugin/framework/FIXME) in the -Framework documentation for details. +output of terraform apply (e.g., the ID of an EC2 instance). This page explains how to migrate computed-only blocks from SDKv2 to the Framework. Refer to [Blocks](/plugin/framework/migrating/attributes-blocks/blocks) if you are looking for information about migrating blocks @@ -17,7 +16,7 @@ that are practitioner configurable. ## SDKv2 -In SDKv2, blocks are defined by an attribute whose type is `TypeList`, or `TypeSet` and whose `Elem` field is set to a +In SDKv2, blocks are defined by an attribute whose type is either `TypeList` or `TypeSet`, and whose `Elem` field is set to a `schema.Resource` that contains a map of the block's attribute names to corresponding `schemaSchema` structs. ```go diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx index c7e3e8760..0a9c8d6a3 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx @@ -24,8 +24,8 @@ func resourceExample() *schema.Resource { "attribute_example": { Required: bool Optional: bool - Computed: bool - Sensitive: bool + Computed: bool + Sensitive: bool /* ... */ ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx index 1e4b532eb..3e4f0ed78 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx @@ -25,7 +25,7 @@ func resourceExample() *schema.Resource { /* ... */ Schema: map[string]*schema.Schema{ "attribute_example": { - ForceNew true + ForceNew: true /* ... */ ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index 8e357021c..d307256e0 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -156,7 +156,7 @@ func (d *httpDataSourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Diag ``` The following examples show how to migrate portions of the -[http](https://github.com/hashicorp/terraform-provider-tls) provider. +[tls](https://github.com/hashicorp/terraform-provider-tls) provider. For a complete example, clone the `terraform-provider-tls` repository and compare the `common_cert.go` file in diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx index 0d3608e8d..2b59ecc3f 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx @@ -25,8 +25,7 @@ greater than one. func resourceExample() *schema.Resource { return &schema.Resource{ /* ... */ - Schema: map[string]*schema.Schema - }{ + Schema: map[string]*schema.Schema{ "attribute_example": { ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)), /* ... */ @@ -37,7 +36,7 @@ func resourceExample() *schema.Resource { In the Framework, you implement either type of validation by setting the `Validators` field on the `tfsdk.Attribute` struct with types that satisfy the `tfsdk.AttributeValidator` interface. -The following example shows the implementation of a validation that ensures that an integer attribute has a value +The following example shows how to implement a validation that ensures that an integer attribute has a value greater than one. ```go diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx index 35ea0fd67..dd758c48f 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -24,8 +24,7 @@ In SDKv2, the `ConflictsWith`, `ExactlyOneOf`, `AtLeastOneOf`, and `RequiredWith func resourceExample() *schema.Resource { return &schema.Resource{ /* ... */ - Schema: map[string]*schema.Schema - }{ + Schema: map[string]*schema.Schema{ "attribute_example": { ConflictsWith: []string ExactlyOneOf: []string @@ -54,10 +53,10 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia ``` Configuration validators can also be defined for -[providers](https://github.com/hashicorp/terraform-plugin-framework-validators/tree/main/providervalidator), -[resources](https://github.com/hashicorp/terraform-plugin-framework-validators/tree/main/resourcevalidator) and -[data sources](https://github.com/hashicorp/terraform-plugin-framework-validators/tree/main/datasourcevalidator) by -implementing `ProviderWithConfigValidators`, `ResourceWithConfigValidators` and `DataSourceWithConfigValidators` +[providers](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/providervalidator), +[resources](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator) and +[data sources](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/datasourcevalidator) by +implementing `ProviderWithConfigValidators`, `ResourceWithConfigValidators`, and `DataSourceWithConfigValidators` interfaces, respectively. ```go @@ -76,12 +75,12 @@ Remember the following details when migrating from SDKv2 to the Framework. - In SDKv2, `ValidateDiagFunc` is a field on `schema.Schema` that you can use to define validation functions. In SDKv2, there are also built-in validations. For example, `ConflictsWith` is a field on the `schema.Schema` struct in SDKv2. In the Framework, `Validators` is a field on each `tfsdk.Attribute` struct. -- Validators replicating the behavior of `ConflictsWith`, `ExactlyOneOf`, `AtLeastOneOf` and `RequiredWith` in SDKv2 are +- Validators replicating the behavior of `ConflictsWith`, `ExactlyOneOf`, `AtLeastOneOf`, and `RequiredWith` in SDKv2 are available for the Framework: - - [ConflictsWith](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/v0.4.0/schemavalidator/conflicts_with.go) - - [ExactlyOneOf](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/v0.4.0/schemavalidator/exactly_one_of.go) - - [AtLeastOneOf](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/v0.4.0/schemavalidator/at_least_one_of.go) - - [AlsoRequires](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/v0.4.0/schemavalidator/also_requires.go) + - [ConflictsWith](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/schemavalidator#ConflictsWith) + - [ExactlyOneOf](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/schemavalidator#ExactlyOneOf) + - [AtLeastOneOf](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/schemavalidator#AtLeastOneOf) + - [AlsoRequires](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/schemavalidator#AlsoRequires) - Define [custom validators](/plugin/framework/migrating/validators-custom) when the predefined validators do not meet your requirements. diff --git a/website/docs/plugin/framework/migrating/data-sources/index.mdx b/website/docs/plugin/framework/migrating/data-sources/index.mdx index d019ce936..fe8a5954a 100644 --- a/website/docs/plugin/framework/migrating/data-sources/index.mdx +++ b/website/docs/plugin/framework/migrating/data-sources/index.mdx @@ -23,7 +23,7 @@ The following example shows a typical implementation. ```go func New() *schema.Provider { return &schema.Provider{ - DataSourcesMap: map[string]*schema.Resource{} + DataSourcesMap: map[string]*schema.Resource{ /* ... */ }, ``` @@ -45,7 +45,7 @@ schema.Resource { ## Framework -In the Framework, you define data sources adding them to your provider's `GetDataSources` function. +In the Framework, you define data sources by adding them to the map returned by your provider's `GetDataSources` function. The `GetDataSources` function on your `provider.Provider` returns a map from the data source name (string) to a type that implements the `DataSourceType` interface for each data source your provider supports. @@ -91,7 +91,7 @@ function on your `datasource.DataSource` type. Remember the following details when completing the migration from SDKv2 to the Framework. - As data sources are read-only, you only implement read functionality for your provider's data sources. Refer to the -[`Read` function](/plugin/framework/resources#read) in the Framework documentation for resources for more details. +[`Read` function](/plugin/framework/resources#read) for resources in the Framework documentation for more details. ## Example diff --git a/website/docs/plugin/framework/migrating/providers/index.mdx b/website/docs/plugin/framework/migrating/providers/index.mdx index c54305507..2188bb5de 100644 --- a/website/docs/plugin/framework/migrating/providers/index.mdx +++ b/website/docs/plugin/framework/migrating/providers/index.mdx @@ -17,7 +17,7 @@ You must update your provider's `main.go` file to serve Framework providers. Ref ### SDKv2 -In SDKv2, the provider packages `main` function serves the provider by calling `plugin.Serve`. +In SDKv2, the provider package's `main` function serves the provider by calling `plugin.Serve`. The following code shows a basic implementation for serving an SDKv2 provider. @@ -286,8 +286,9 @@ The following example defines the provider schema in the `Schema` field within t ```go func New() *schema.Provider { return &schema.Provider{ - Schema: map[string]*schema.Schema{}, - /* ... */ + Schema: map[string]*schema.Schema{ + /* ... */ + }, ``` ### Framework @@ -302,7 +303,9 @@ The following code shows the `GetSchema` function, which returns the provider sc ```go func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{}, nil + return tfsdk.Schema{ + /* ... */ + }, nil } ``` @@ -314,8 +317,8 @@ those fields to the Framework. Remember the following differences between SDKv2 and the Framework when completing the migration. -In SDKv2, `schema.Schema` is a struct that defines attributes and behaviors (e.g., `Type`, `Optional`). In the -Framework `tfsdk.Schema` is a struct that defines `Attributes`, which are structs that define attributes and behaviors +- In SDKv2, `schema.Schema` is a struct that defines attributes and behaviors (e.g., `Type`, `Optional`). In the +Framework `tfsdk.Schema` is a struct that includes `tfsdk.Attributes`, which are structs that define attributes and behaviors (e.g., `Type`, `Optional`). ### Example @@ -334,7 +337,7 @@ respectively. Refer to the #### SDKv2 -The following example shows how to configure an attribute within a provider schema using SDKv2. +The following example from the `provider.go` file shows the configuration of the `url` attribute for the provider's`proxy` configuration block. ```go Schema: map[string]*schema.Schema{ @@ -357,7 +360,9 @@ Schema: map[string]*schema.Schema{ #### Framework -The following example shows how to configure provider schema using the Framework. +The following shows the same section of provider code after the migration. + +This code implements the `url` attribute for the `proxy` block with the Framework. ```go func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { diff --git a/website/docs/plugin/framework/migrating/resources/crud.mdx b/website/docs/plugin/framework/migrating/resources/crud.mdx index 840b13bc8..83690d5c1 100644 --- a/website/docs/plugin/framework/migrating/resources/crud.mdx +++ b/website/docs/plugin/framework/migrating/resources/crud.mdx @@ -128,7 +128,7 @@ func createPassword(ctx context.Context, d *schema.ResourceData, meta interface{ ### Framework The following shows the same section of provider code after the migration. -This code implements the `Create()` function for the `random_password` resource with the Framework. +This code implements the `Create` function for the `random_password` resource with the Framework. ```go func (r *passwordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index f435af27a..617e9eb08 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -30,12 +30,12 @@ func resourceExample() *schema.Resource { The `StateContextFunc` is the function called to import a resource into Terraform state. Any operations that are needed to import the resource take place within this function and may result in the mutation of the `ResourceData` -that is passed into the function. The return value is a slice of `schema.ResourceData`. This slice, which may be simple, likemight be as simple as returning the `ResourceData` that was passed into the function, or it may involve multipleinvolve fan out to multiple resources, such as AWS security groups. +that is passed into the function. The return value is a slice of `schema.ResourceData`. This slice might be as simple as returning the `ResourceData` that was passed into the function, or it may involve multiple fan out to multiple resources, such as AWS security groups. ## Framework In the Framework, you implement the `ResourceWithImportState` interface on your `resource.Resource` type to allow -users to import a given resource. +users to import a given resource. This interface requires that your type implement a `ImportState` function. The following code shows how you define an `ImportState` function with the Framework. @@ -53,15 +53,14 @@ state by mutating the state. Remember the following differences between SDKv2 and the Framework when completing the migration. - In both SDKv2 and Framework, there is no access to the configuration, state, or plan during import. The import -functions can only access the value (e.g., `ID`) supplied to the `terraform import` command. +functions can only access the value (e.g., the resource's `ID`) supplied to the `terraform import` command. - In SDKv2, you implement resource importing by populating the `Importer` field on the `schema.Resource` struct. In the Framework, you define an `ImportState` function on the type which implements `resource.Resource`. This implementation satisfies the `tfsdk.ResourceWithImportState` interface. ## Example -In the simple use case in which `schema.ImportStatePassthroughContext` has been used with SDKv2, migrating simply -involves using `resource.ImportStatePassthroughID`. +In the simple use case in which `schema.ImportStatePassthroughContext` has been used with SDKv2, migrating to the Framework involves using the `resource.ImportStatePassthroughID` function. ### SDKv2 @@ -79,8 +78,6 @@ func resourceExample() *schema.Resource { ### Framework ```go - - func (r *resourceExample) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } diff --git a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx index 15b9955cd..a014a6895 100644 --- a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx +++ b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx @@ -27,7 +27,7 @@ func resourceExample() *schema.Resource { In the Framework, you implement plan modification either by implementing the `ResourceWithModifyPlan` interface on your resource type, or by implementing `PlanModifiers` on individual attributes. This page demonstrates how to implement the -`Plan Modifiers` on individual attributes. Refer to +plan modifiers on individual attributes. Refer to [Attributes - Default Values](/plugin/framework/migrating/attributes#default-values) and [Attributes - Force New](/plugin/framework/migrating/attributes#force-new) in this guide for further information on how to implement a plan modifier on an attribute. diff --git a/website/docs/plugin/framework/migrating/schema/index.mdx b/website/docs/plugin/framework/migrating/schema/index.mdx index 1b99b14dc..afeb1dc89 100644 --- a/website/docs/plugin/framework/migrating/schema/index.mdx +++ b/website/docs/plugin/framework/migrating/schema/index.mdx @@ -10,7 +10,7 @@ Providers, resources, and data sources all use schema to define their attributes constraints of Terraform configuration blocks and how the provider, resource, or data source behaves. Refer to [Schemas](/plugin/framework/schemas) in the Framework documentation for details. -This page explains the differences between the schema used by SDKv2 and the Framework. For further schema-specific documentation, refer to the other migration guides in this section: +This page explains the differences between the schema used by SDKv2 and the Framework. We also recommend reviewing these additional schema guides throughout the migration: - [Attributes](/plugin/framework/migrating/attribute-blocks/attribute-schema) where the schema defines practitioner or provider data associated with a value and type. - [Attribute types](/plugin/framework/migrating/attribute-blocks/types) where the schema defines the expected data structure and syntax. diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index 740cd86f6..3b7aa73a7 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -14,7 +14,7 @@ been altered by the migration itself. You will also need to [update](#provider-f During migration, we recommend writing tests to verify that switching from SDKv2 to the Framework has not affected your provider's behavior. These tests use identical configuration. The first test step applies a plan and generates state with the SDKv2 version of the provider. The second test step generates a plan with the Framework version of the provider -and verifies that the plan is a no-op indicating that migrating to the framework has not altered behaviour. +and verifies that the plan is a no-op, indicating that migrating to the framework has not altered behaviour. Use the `ExternalProviders` field within a `resource.TestStep` to specify the configuration of a specific provider to use during each test step. You can specify a version of the provider built on SDKv2 during the first test step, and @@ -111,8 +111,8 @@ The following examples show how to migrate portions of the [http](https://github provider. To review a complete example, clone the -`terraform-provider-http` repository and compare -[v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source_test.go) with +`terraform-provider-http` repository and compare the `data_source_test.go` file in +[v2.2.0](https://github.com/hashicorp/terraform-provider-http/blob/v2.2.0/internal/provider/data_source_test.go) with the `data_source_http_test.go` file in [v3.0.1](https://github.com/hashicorp/terraform-provider-http/blob/v3.0.1/internal/provider/data_source_http_test.go). #### SDKv2 From f07b3e2540f96cf1c120e146d3d06221d0c849e6 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 8 Sep 2022 12:13:21 +0100 Subject: [PATCH 26/28] Replacing link using "Provider Schema" with "Providers" (#459) --- .../plugin/framework/migrating/attributes-blocks/types.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index d307256e0..5cca4d6a3 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -12,7 +12,8 @@ that contain other attributes are referred to as nested attributes, and are impl [Schemas - Attributes](/plugin/framework/schemas#type) in the Framework documentation for details. This page explains how to migrate a primitive attribute from SDKv2 to the plugin Framework. For an example of -migrating a nested block to a nested attribute, refer to [Provider Schema](/plugin/framework/migrating/providers#example-1) in the Framework documentation. +migrating a nested block to a nested attribute, refer to [Providers](/plugin/framework/migrating/providers#example-1) in +this guide. ## SDKv2 @@ -108,7 +109,7 @@ Remember the following differences between SDKv2 and the Framework when completi - In the Framework, an `Attribute` struct has either the `Type` or `Attributes` field defined. The `Type` field is set to a primitive type such as an integer or string, and you use the `Attributes` field for `NestedAttributes`. Refer to -[Provider Schema](/plugin/framework/migrating/providers#example-1) for an example of using a single +[Providers](/plugin/framework/migrating/providers#example-1) for an example of using a single nested attribute. Nested attributes are also described in more detail on the [Schemas](https://www.terraform.io/plugin/framework/schemas#attributes-1) page in the Framework documentation. From d14ee0d7ef312b8ebfa96bd849a1e19bd36fec97 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 8 Sep 2022 12:56:59 +0100 Subject: [PATCH 27/28] Fixing formatting and wording (#459) --- .../migrating/attributes-blocks/types.mdx | 18 +++---- .../validators-predefined.mdx | 10 ++-- .../migrating/data-sources/index.mdx | 4 +- .../framework/migrating/resources/import.mdx | 3 +- .../framework/migrating/resources/index.mdx | 51 ++++++++++--------- .../migrating/resources/state-upgrade.mdx | 2 +- .../plugin/framework/migrating/testing.mdx | 2 +- 7 files changed, 46 insertions(+), 44 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index 5cca4d6a3..e3139ba09 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -28,31 +28,31 @@ func resourceExample() *schema.Resource { Type: schema.TypeBool, /* ... */ }, - "float64_example": { + "float64_example": { Type: schema.TypeFloat, /* ... */ }, - "int64_example": { + "int64_example": { Type: schema.TypeInt, /* ... */ }, - "list_example": { + "list_example": { Type: schema.TypeList, - Elem: &schema.Schema{ + Elem: &schema.Schema{ Type: schema.TypeBool, }, /* ... */ }, - "map_example": { + "map_example": { Type: schema.TypeMap, - Elem: &schema.Schema{ + Elem: &schema.Schema{ Type: schema.TypeFloat, }, /* ... */ }, - "set_example": { + "set_example": { Type: schema.TypeSet, - Elem: &schema.Schema{ + Elem: &schema.Schema{ Type: schema.TypeInt, }, /* ... */ @@ -176,7 +176,7 @@ func resourceCertRequest() *schema.Resource { Schema: map[string]*schema.Schema{ "dns_names": { Type: schema.TypeList, - Elem: &schema.Schema{ + Elem: &schema.Schema{ Type: schema.TypeString, }, /* ... */ diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx index dd758c48f..5057310b2 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -26,10 +26,10 @@ func resourceExample() *schema.Resource { /* ... */ Schema: map[string]*schema.Schema{ "attribute_example": { - ConflictsWith: []string - ExactlyOneOf: []string - AtLeastOneOf: []string - RequiredWith: []string + ConflictsWith: []string{ /* ... */ }, + ExactlyOneOf: []string{ /* ... */ }, + AtLeastOneOf: []string{ /* ... */ }, + RequiredWith: []string{ /* ... */ }, /* ... */ ``` ## Framework @@ -48,7 +48,7 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia Attributes: map[string]tfsdk.Attribute{ "attribute_example": { Validators: []tfsdk.AttributeValidator{ - schemavalidator.ConflictsWith(), + schemavalidator.ConflictsWith( /* ... */ ), /* ... */ ``` diff --git a/website/docs/plugin/framework/migrating/data-sources/index.mdx b/website/docs/plugin/framework/migrating/data-sources/index.mdx index fe8a5954a..ae8ceb55e 100644 --- a/website/docs/plugin/framework/migrating/data-sources/index.mdx +++ b/website/docs/plugin/framework/migrating/data-sources/index.mdx @@ -54,7 +54,9 @@ The following code shows how you add a data source to your provider with the Fra ```go func (p *provider) GetDataSources(ctx context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { - return map[string]provider.DataSourceType{}, nil + return map[string]provider.DataSourceType{ + /* ... */ + }, nil } ``` diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index 617e9eb08..ea8503b74 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -45,8 +45,7 @@ func (r *resourceExample) ImportState(ctx context.Context, req resource.ImportSt } ``` -The `ImportState` function includes a `resource.ImportStateResponse`, which you use to mutate and set your resource's -state by mutating the state. +The `ImportState` function includes a `resource.ImportStateResponse`, which you use to set your resource's state. ## Migration Notes diff --git a/website/docs/plugin/framework/migrating/resources/index.mdx b/website/docs/plugin/framework/migrating/resources/index.mdx index 553f82155..b3a35e899 100644 --- a/website/docs/plugin/framework/migrating/resources/index.mdx +++ b/website/docs/plugin/framework/migrating/resources/index.mdx @@ -31,12 +31,13 @@ The following code shows a basic implementation of resource schema with SDKv2. ```go func New() *schema.Provider { return &schema.Provider{ - ResourcesMap: map[string]*schema.Resource - }{ - "resource_example": resourceExample(), + ResourcesMap: map[string]*schema.Resource { + "resource_example": resourceExample(), + /* ... */ + }, + /* ... */ } - /* ... */ -}, +} ``` SDKv2 defines the `schema.Resource` struct as follows. @@ -44,28 +45,28 @@ SDKv2 defines the `schema.Resource` struct as follows. ```go schema.Resource{ Schema map[string]*Schema - SchemaVersion int + SchemaVersion int MigrateState StateMigrateFunc - StateUpgraders []StateUpgrader - Create CreateFunc + StateUpgraders []StateUpgrader + Create CreateFunc Read ReadFunc - Update UpdateFunc - Delete DeleteFunc - Exists ExistsFunc - CreateContext CreateContextFunc - ReadContext ReadContextFunc - UpdateContext UpdateContextFunc - DeleteContext DeleteContextFunc - CreateWithoutTimeout CreateContextFunc - ReadWithoutTimeout ReadContextFunc - UpdateWithoutTimeout UpdateContextFunc + Update UpdateFunc + Delete DeleteFunc + Exists ExistsFunc + CreateContext CreateContextFunc + ReadContext ReadContextFunc + UpdateContext UpdateContextFunc + DeleteContext DeleteContextFunc + CreateWithoutTimeout CreateContextFunc + ReadWithoutTimeout ReadContextFunc + UpdateWithoutTimeout UpdateContextFunc DeleteWithoutTimeout DeleteContextFunc - CustomizeDiff CustomizeDiffFunc + CustomizeDiff CustomizeDiffFunc Importer *ResourceImporter - DeprecationMessage string + DeprecationMessage string Timeouts *ResourceTimeout - Description string - UseJSONNumber bool + Description string + UseJSONNumber bool } ``` @@ -114,9 +115,9 @@ The `tfsdk.Schema` is used for defining the schema for providers, resources and ```go type Schema struct { - Attributes map[string]Attribute - Blocks map[string]Block - Version int64 + Attributes map[string]Attribute + Blocks map[string]Block + Version int64 DeprecationMessage string Description string MarkdownDescription string diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx index fa1240f63..9b5e8c7be 100644 --- a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -8,7 +8,7 @@ description: >- # State Upgraders When you update a resource's implementation in your provider, some changes may not be compatible with old versions. You -can create state upgraders to help users migrate resources provisioned with old schema configurations. Refer to +can create state upgraders to automatically migrate resources provisioned with old schema configurations. Refer to [State Upgrade](/plugin/framework/resources/state-upgrade) in the Framework documentation for details. This page explains how to migrate resource `StateUpgraders` in SDKv2 to `UpgradeState` in the plugin Framework. diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index 3b7aa73a7..636030582 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -21,7 +21,7 @@ use during each test step. You can specify a version of the provider built on SD then you can use the version of the provider built on the Framework in subsequent test step(s) to verify that Terraform CLI does not detect any planned changes. -You must also update the [provider factories](/plugin/framework/migrating/testing/provider-factories) to use +You must also update the [provider factories](#provider-factories) to use the Framework. ### Example From 73879d7d74c945bbfe63909654681efe4ff3607c Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 8 Sep 2022 16:56:34 +0100 Subject: [PATCH 28/28] Fixing formatting and wording (#459) --- .../migrating/attributes-blocks/blocks-computed.mdx | 4 ++-- .../migrating/attributes-blocks/blocks.mdx | 2 +- .../plugin/framework/migrating/providers/index.mdx | 13 +++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 0fc395985..20fbf3505 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -1,10 +1,10 @@ --- page_title: 'Computed Blocks: Migrating from SDKv2 to the Framework' description: >- - Migrate computed blocks from SDKv2 to attribute validators in the plugin Framework. + Migrate blocks with computed fields from SDKv2 to attribute validators in the plugin Framework. --- -# Computed Blocks +# Blocks with Computed Fields Some providers, resources, and data sources include repeatable nested blocks in their attributes. Some blocks contain fields with `Computed: true`, which means that the provider code can define the value or that it could come from the diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx index 75edc7a45..a18b2e473 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx @@ -11,7 +11,7 @@ typically represent separate objects that are related to (or embedded within) th This page explains how to migrate nested blocks that are not computed (i.e., do not set `Computed: true`) from SDKv2 to the Framework. Refer to -[Computed Blocks](/plugin/framework/migrating/attributes-blocks/blocks-computed) for more details +[Blocks with Computed Fields](/plugin/framework/migrating/attributes-blocks/blocks-computed) for more details about migrating nested blocks that contain fields that are computed. ## Nested Block Example diff --git a/website/docs/plugin/framework/migrating/providers/index.mdx b/website/docs/plugin/framework/migrating/providers/index.mdx index 2188bb5de..db66b1baa 100644 --- a/website/docs/plugin/framework/migrating/providers/index.mdx +++ b/website/docs/plugin/framework/migrating/providers/index.mdx @@ -78,7 +78,7 @@ func main() { providers := []func() tfprotov5.ProviderServer{ providerserver.NewProtocol5(provider.New()), - tftime.Provider().GRPCProvider, + provider.Provider().GRPCProvider, } muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...) @@ -171,7 +171,7 @@ func (p *provider) GetResources(ctx context.Context) (map[string]provider.Resour func (p *provider) GetDataSources(ctx context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { return map[string]provider.DataSourceType{ "data_source_example": dataSourceTypeExample{}, -}, nil + }, nil } ``` @@ -191,7 +191,7 @@ names to `schema.Resource` structs. In the Framework, `GetResources` is a functi returns `map[string]provider.ResourceType`, which maps resource names to types that you define, which satisfy the `provider.ResourceType` interface. - In SDKv2, `DataSourcesMap` is a field on `schema.Provider` containing `map[string]*schema.Resource`, which maps data -source names to `schema.Resource` structs (Data sources and resources both use `schema.Resource`). In the Framework, +source names to `schema.Resource` structs (data sources and resources both use `schema.Resource`). In the Framework, `GetDataSources` is a function you define on your provider that returns `map[string]provider.DataSourceType`, which maps data source names to types that you define, which satisfy the `provider.DataSourceType` interface. @@ -214,7 +214,7 @@ func New() (*schema.Provider, error) { return &schema.Provider{ Schema: map[string]*schema.Schema{ "proxy": { - /* ... */ + /* ... */ }, }, ConfigureContextFunc: configureProvider, @@ -259,7 +259,7 @@ func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) return tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ "proxy": { - /* ... */ + /* ... */ }, }, }, nil @@ -333,7 +333,8 @@ with [v4.0.1](https://github.com/hashicorp/terraform-provider-tls/blob/v4.0.1/in This example also shows how to use a nested block and a nested attribute for the SDKv2 and Framework examples, respectively. Refer to the -[Blocks and Nested Attributes](/plugin/framework/migrating/attributes-blocks) page in this guide for more details. +[Blocks with Computed Fields](/plugin/framework/migrating/attributes-blocks/blocks-computed) page in this guide for more +details. #### SDKv2