-
Notifications
You must be signed in to change notification settings - Fork 231
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
[DRAFT] Add support for write only attributes #1375
base: main
Are you sure you want to change the base?
Conversation
… `PlanResourceChange()`
…plement validation in `ValidateResourceTypeConfig()` RPC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of this is looking awesome, great work! I left some initial comments, but in addition to that:
- I'm wondering what will eventually show up in the plan renderer for Terraform... since we are planning non-null values that will eventually be set to null. Feels like it should either explicitly mention that the value is write-only, or we should be planning
null
for write-only attributes as well. We should make a note to come back to this once core gets further along. - It'd be cool to get some corner testing for SDKv2 to catch any regressions we might introduce in the future
- I'd love to run this development branch against a major cloud TF provider (aws/gcp/azure) if we can get a hand from their teams finding the appropriate CI jobs to do so. They have so many SDKv2 schemas + tests that it would be a good smoke test.
- We would need them to update their plugin-go/mux/etc, so you might need to adjust the plugin-go branch to make that all compile.
func (s *GRPCProviderServer) ValidateEphemeralResourceConfig(ctx context.Context, req *tfprotov5.ValidateEphemeralResourceConfigRequest) (*tfprotov5.ValidateEphemeralResourceConfigResponse, error) { | ||
ctx = logging.InitContext(ctx) | ||
|
||
logging.HelperSchemaTrace(ctx, "Returning error for provider validate ephemeral resource call") | ||
|
||
resp := &tfprotov5.ValidateEphemeralResourceConfigResponse{} | ||
|
||
resp.Diagnostics = []*tfprotov5.Diagnostic{ | ||
{ | ||
Severity: tfprotov5.DiagnosticSeverityError, | ||
Summary: "Ephemeral Resource Not Found ", | ||
Detail: fmt.Sprintf("No ephemeral resource type named %q was found in the provider", req.TypeName), | ||
}, | ||
} | ||
|
||
return resp, nil | ||
} | ||
|
||
func (s *GRPCProviderServer) OpenEphemeralResource(ctx context.Context, req *tfprotov5.OpenEphemeralResourceRequest) (*tfprotov5.OpenEphemeralResourceResponse, error) { | ||
ctx = logging.InitContext(ctx) | ||
|
||
logging.HelperSchemaTrace(ctx, "Returning error for provider open ephemeral resource call") | ||
|
||
resp := &tfprotov5.OpenEphemeralResourceResponse{} | ||
|
||
resp.Diagnostics = []*tfprotov5.Diagnostic{ | ||
{ | ||
Severity: tfprotov5.DiagnosticSeverityError, | ||
Summary: "Ephemeral Resource Not Found ", | ||
Detail: fmt.Sprintf("No ephemeral resource type named %q was found in the provider", req.TypeName), | ||
}, | ||
} | ||
|
||
return resp, nil | ||
} | ||
|
||
func (s *GRPCProviderServer) RenewEphemeralResource(ctx context.Context, req *tfprotov5.RenewEphemeralResourceRequest) (*tfprotov5.RenewEphemeralResourceResponse, error) { | ||
ctx = logging.InitContext(ctx) | ||
|
||
logging.HelperSchemaTrace(ctx, "Returning error for provider renew ephemeral resource call") | ||
|
||
resp := &tfprotov5.RenewEphemeralResourceResponse{} | ||
|
||
resp.Diagnostics = []*tfprotov5.Diagnostic{ | ||
{ | ||
Severity: tfprotov5.DiagnosticSeverityError, | ||
Summary: "Ephemeral Resource Not Found ", | ||
Detail: fmt.Sprintf("No ephemeral resource type named %q was found in the provider", req.TypeName), | ||
}, | ||
} | ||
|
||
return resp, nil | ||
} | ||
|
||
func (s *GRPCProviderServer) CloseEphemeralResource(ctx context.Context, req *tfprotov5.CloseEphemeralResourceRequest) (*tfprotov5.CloseEphemeralResourceResponse, error) { | ||
ctx = logging.InitContext(ctx) | ||
|
||
logging.HelperSchemaTrace(ctx, "Returning error for provider close ephemeral resource call") | ||
|
||
resp := &tfprotov5.CloseEphemeralResourceResponse{} | ||
|
||
resp.Diagnostics = []*tfprotov5.Diagnostic{ | ||
{ | ||
Severity: tfprotov5.DiagnosticSeverityError, | ||
Summary: "Ephemeral Resource Not Found ", | ||
Detail: fmt.Sprintf("No ephemeral resource type named %q was found in the provider", req.TypeName), | ||
}, | ||
} | ||
|
||
return resp, nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're on the same page, but just to note, these protocol changes will come in a published terraform-plugin-go
version before write-only, so we might just want to publish a terraform-plugin-sdk/v2
version with these functions at that time.
helper/validation/write_only.go
Outdated
Detail: fmt.Sprintf("The attribute %s has a WriteOnly version %s available. "+ | ||
"Use the WriteOnly version of the attribute when possible.", attrStep.Name, writeOnlyAttributeName), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to bikeshed this warning message wording with the provider development teams
return | ||
} | ||
|
||
if !attr.value.IsNull() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be interesting to test this, but I have a feeling for practitioners that are already using the resource, the SDK won't be able to completely null this value out. We may have to check the cty.Value against zero values as well 😞 (empty string, zero, etc.)
I don't know off the top of my head if that's valid, but I'm pretty sure there's nothing a provider dev/practitioner can do to null a value in SDKv2 once it has a value 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A corner provider test could shine some light on this problem 💭
}, | ||
}, | ||
}, | ||
"map nested block: oldAttribute and writeOnlyAttribute map returns warning diags": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar question to one I had above, are map nested blocks in SDKv2? Or is this a map attribute equivalent
helper/schema/grpc_provider.go
Outdated
@@ -1182,6 +1219,8 @@ func (s *GRPCProviderServer) ApplyResourceChange(ctx context.Context, req *tfpro | |||
|
|||
newStateVal = copyTimeoutValues(newStateVal, plannedStateVal) | |||
|
|||
newStateVal = setWriteOnlyNullValues(newStateVal, schemaBlock) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should call setWriteOnlyNullValues
in the ReadResource
RPC as well? It's possible the provider could attempt to refresh the state with a value in a write-only attribute. Terraform core doesn't typically do any validation/assertions then, so there wouldn't be an error.
That seems like being potentially defensive, but we did have a bug about unknown values making it state during refresh recently lol:
- See outcome and linked issues for more context: Consider Explicit Error Diagnostics When MoveResourceState/ReadResource/UpgradeResourceState Response State Includes Unknown Value terraform-plugin-framework#902
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will it be possible for a non-null write only attribute in ReadResource
to affect state? According to the current proposal, write-only attributes will be completely omitted from the state file, so I would be surprised that Terraform Core would write the value even if we do send one across the wire.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current relationship between core and the providers is that the provider is responsible for producing data that satisfies Terraform's constraints. In the case that the provider doesn't, Terraform raises an error diagnostic, rather than silently ignoring the incorrect data. ReadResource
doesn't necessarily have constraints today, since a following plan will converge any data that has drifted. However write-only attributes should never get written to state, so I would imagine that this represents a new constraint that ReadResource
should also apply, which means you'd get an error diagnostic for attempting to write a write-only attribute to state.
However, we are discussing logic that doesn't exist yet in core, so it's possible they won't take this route. Perhaps we can just note to revisit this when core is closer to implementation
Co-authored-by: Austin Valle <[email protected]>
…teRawResourceConfigFuncs`
No description provided.