-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Investigate Consolidation of Schema Required/Optional/Computed #31
Comments
While pondering this, it could also be interesting to ponder whether
I've not thought exhaustively about this so there might well be some other useful benefit to |
Just to capture the data point, core does not implement configurability validations, per hashicorp/terraform#30669. We did implement "Required" and "Read-Only" checks in v0.9.0 of the framework, per #370. While looking at another issue involving validation, it reminded me that many of our "common use case" validators that will wind up in github.com/hashicorp/terraform-plugin-framework-validators will purposefully skip null values, because they are expecting something else to handle any "Required" checking. This makes me wonder if we could extend the simplification even further:
This would allow provider developers to define all expected configuration value information in one place (attribute validators). I haven't really thought through whether this is desirable or would have other issues. There's also another similar potential simplification:
I'm not sure when we'll really dive into this, but wanted to document the related issue, pull request, and ideas while thinking about it. |
@bflad One concern about changing these attributes from declarative to imperative manner is that makes the static analysis tools (e.g. tfproviderlint) hard to implement. What do you think about this? |
Reference: hashicorp#31 Reference: hashicorp#132 Reference: hashicorp#223 Reference: hashicorp#326 Reference: hashicorp#365 Reference: hashicorp#389 The main goals of this change are: - Prepare the Go module to support multiple packages that implement concept-specific schema declarations, in particular the `datasource`, `provider`, and `resource` packages - Continue supporting the existing `tfsdk` package schema implementation with minimal provider developer breaking changes, allowing a deprecation period when the concept-specific schemas are introduced - Migrate unexported or unintentially exported `tfsdk` schema functionality to internal packages These goals are accomplished by creating new `internal/fwschema` and `internal/fwxschema` packages, which contain interface types that the provider developer facing packages implement. Currently, the `tfsdk` package is the only implementation, until the design of those concept-specific schema declarations is fully determined. Those designs may include changes to schema implementation details, which cannot be accomplished in the existing `tfsdk` implementation without breaking it and provider developers migrating code to the new packages is a great time to make those types of changes. The internal interface method design here is purposefully similar to the existing `tfsdk` implementations as we cannot name the methods the same as existing fields and to reduce review burden. After the `tfsdk` implementations are removed, we can consider tidying up the interface methods to drop the `Get` and `Is` method name prefixes. There are some minor followup changes that will happen either between or during concept-specific schema implementation work, namely: - Swapping calls `tfsdk` package type `TerraformType()` methods for `AttributeType().TerraformType()` to reduce implementation work for new schema implementations - Updating unit testing that relies on `tfsdk.Schema` to set up sub-tests via `(*testing.T).Run()` to against multiple implementations These were not included here to reduce the review burden as they are separate details which can be handled later.
….0 (hashicorp#31) Bumps [github.com/hashicorp/terraform-plugin-sdk/v2](https://github.com/hashicorp/terraform-plugin-sdk) from 2.12.0 to 2.13.0. - [Release notes](https://github.com/hashicorp/terraform-plugin-sdk/releases) - [Changelog](https://github.com/hashicorp/terraform-plugin-sdk/blob/main/CHANGELOG.md) - [Commits](hashicorp/terraform-plugin-sdk@v2.12.0...v2.13.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/terraform-plugin-sdk/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@magodo it is certainly a concern, but the framework design has generally tried to prevent the need for static analysis tooling on framework code implementations in providers by making errant implementations not possible for developers to implement. This attribute definition triple flag situation is one of the remaining places that was not adjusted before the framework had a stable release. Did you have specific static analysis use cases in mind that replacing these flags with a single configuration would make problematic? Honestly, I'm more concerned about other types of downstream tooling that relies on this information, should we decide to change parts of how its represented, since there is conflicting meanings to the flags between Terraform core and the provider side of the protocol. |
Yet another drive-by comment 🙁, but I want to ensure this is captured in a form as it relates to this issue. #898 highlights another interesting case in this space. Both the prior SDK (with blocks errantly being able to be defined as computed) and framework (with nested attributes being able to be defined as computed with non-computed underlying attributes) conflict with how Terraform core wants providers to define their schemas in comparison to how they might be perceived by provider developers. While the implementation of the framework prevents the block case by not exposing computed (or optional or required) as an option for provider developers, there is nothing preventing provider developers from implementing computed nested attributes with non-computed underlying attributes. From a provider developer experience perspective, this seems like it should be valid and supported. "My system supports configuration of a collection of objects or will compute them, but when configured, inside those objects we want to require configuration of some parts." So you create a schema definition such as: // comments from the provider developers perspective
schema.SetNestedAttribute{
Optional: true, // this collection may be supplied via configuration
Computed: true, // this collection may be supplied by the system when not configured
PlanModifiers: []planmodifier.Set{ // system behavior specific
setplanmodifier.UseStateForUnknown(), // preserve the system value on updates (it does not change)
},
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"name": schema.StringAttribute{
Required: true, // this must be configured, when the object is configured
},
},
},
}, As of Terraform 1.6.x though, the core algorithm for generating the proposed new state won't act the same as a regular computed attribute in this case when the nested attribute configuration is null. Adjusting the core algorithm may be safe for this use case (ignore computability in underlying attributes if the nested attribute is computed, configuration of the nested attribute is null, and there is a prior state), but that sort of change would only apply to environments running versions of Terraform after the change. We've talked about potentially moving the proposed new state algorithm into the framework since it has all the provider-side context (e.g. default values) to enhance planning, which would resolve that sort of Terraform versioning issue, but its a quite risky change. Another, not necessarily mutually exclusive, option we have is to prevent provider developers from getting into this awkward situation by raising a new "implementation" error when we detect non-computed attributes under a computed nested attribute. Luckily the attribute-based validation abstractions in the framework operate when "this nesting level is configured", so its possible to add a "not null" validator similar to the framework's internal There is also potential for the framework's type system to let developers say "this value should never be null" from a type-perspective as another, not mutually exclusive, option. The type-based validation would theoretically never trigger until the object for the value exists, giving the same behavior as the prior option. One wrinkle here is that type definitions can exist within other type definitions, meaning the framework might need to walk entire values in more places than it does today. To somewhat prevent this particular implementation issue (it wouldn't fix the missing computed 😖 ), we could consider removing required as a flag given to provider developers. Instead, we would make "required" (not null) a validator or type behavior you choose to add, like you do for other configuration validation rules on attribute values. We would want to verify that switching from required to optional like this won't fundamentally break anything from core's perspective with practitioner configuration behaviors or planning internals if provider developers do this unilaterally. There are unfortunate consequences though, as things like documentation generation and editor integrations rely on that flag to immediately give practitioners that configuration expectation, which has somewhat higher urgency than other validations on the value itself. If that is something we wanted to do, then we would mark the If something like that was to happen, what we would be left with (barring the protocol changing) would be debating the merits of whether attributes would have a "default" configurability that provider developers could override if necessary or if we still want to require provider developers to explicitly state an intention every attribute. Either of those options feel like they could use a single attribute definition field as originally proposed here, just with slightly different naming thats tailored for provider developers, e.g. read-only/provider-only (computed), read-write/configuration-or-provider (computed and optional), and potentially write-only/configuration-only (optional). |
Module version
Use-cases
In Terraform Plugin SDK and v0.1.0 of this project, there are currently only a few meaningful valid configurations of
Required
,Optional
, andComputed
for schema attributes. Switching these to a single field that captures the meaningful valid configurations means there is no way to misconfigure them and it is not losing any configurability. Setting these asfalse
is a perfectly valid implementation, just conventionally shied away from in most current schemas due to its unnecessary verbosity.Mapping out the three possible boolean field declarations:
Required: true
: ✅Required: false
: ❌Required: true
+Optional: true
: ❌Required: true
+Optional: false
: 👍 (just verbose)Required: false
+Optional: true
: 👍 (just verbose)Required: false
+Optional: false
: ❌Required: true
+Computed: true
: ❌Required: true
+Computed: false
: 👍 (just verbose)Required: false
+Computed: true
: 👍 (just verbose)Required: false
+Computed: false
: ❌Optional: true
: ✅Optional: false
: ❌Optional: true
+Computed: true
: ✅Optional: false
+Computed: true
: 👍 (just verbose)Optional: true
+Computed: false
: 👍 (just verbose)Optional: false
+Computed: false
: ❌Computed: true
: ✅Computed: false
: ❌In terms of static analysis, it is much easier to deal with one required field than three optional and conflicting ones. For example, it is relatively easy to write
go/analysis
andsemgrep
tooling that checks for the existence of a field declaration with a struct. Tooling and ensuring consistency around that is harder if you need to check values, let alone multiple of them, to ensure validity and any stylistic conventions. Not the biggest deal, but preferring nofalse
values for the current booleans requires additional static analysis, for example: https://github.com/bflad/tfproviderlint/tree/main/passes/S019.Proposal
Quick sketch being:
Which the naming can be adjusted as necessary, e.g. can be further reduced with any of dropping Attribute, Attribute to Attr, Configuration to Config, moving to a separate package, etc.
References
The text was updated successfully, but these errors were encountered: