Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

all: Add automatic deferred action support for unknown provider configuration #1002

Merged
merged 51 commits into from
May 30, 2024

Conversation

SBGoods
Copy link
Contributor

@SBGoods SBGoods commented May 16, 2024

Ref: hashicorp/terraform-plugin-go#403

This PR implements deferred action support for PROVIDER_CONFIG_UNKNOWN during the ConfigureProvider RPC. This functionality will allow a provider to defer all resources and data sources associated with the framework provider during the ReadResource, ReadDataSource, ImportResourceState, and PlanResourceChange RPCs.

Deferred actions is an experimental feature introduced in Terraform v1.9.0-alpha20240404 and is only available in experimental Terraform builds.

SBGoods and others added 30 commits May 1, 2024 15:35
…d `DeferredResponse` respectively to match protobuf definition
…s/automatic-deferred-action-support

# Conflicts:
#	internal/fromproto5/client_capabilities.go
#	internal/fromproto6/client_capabilities.go
@SBGoods SBGoods changed the title [DRAFT] All: Add automatic deferred action support for unknown provider configuration all: Add automatic deferred action support for unknown provider configuration May 20, 2024
@SBGoods SBGoods marked this pull request as ready for review May 20, 2024 22:11
@SBGoods SBGoods requested a review from a team as a code owner May 20, 2024 22:11
@SBGoods SBGoods requested a review from austinvalle May 21, 2024 15:21
Copy link
Member

@austinvalle austinvalle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Some general notes and a comment about plan modification where we might want to adjust some of our docs/naming

internal/fwserver/server.go Outdated Show resolved Hide resolved
internal/fwserver/server_configureprovider.go Outdated Show resolved Hide resolved
internal/fwserver/server_importresourcestate.go Outdated Show resolved Hide resolved
internal/fwserver/server_importresourcestate_test.go Outdated Show resolved Hide resolved
internal/fwserver/server_readdatasource_test.go Outdated Show resolved Hide resolved
internal/fwserver/server_planresourcechange_test.go Outdated Show resolved Hide resolved
internal/fwserver/server_readresource_test.go Outdated Show resolved Hide resolved
resource/metadata.go Show resolved Hide resolved
Comment on lines 254 to 262
// Skip resource-level ModifyPlan for automatic deferrals
// unless ProviderDeferredBehavior.EnablePlanModification is true
if s.deferred != nil && !req.ResourceBehavior.ProviderDeferred.EnablePlanModification {
logging.FrameworkDebug(ctx, "Provider has deferred response configured, automatically returning deferred response.")
resp.Deferred = &resource.Deferred{
Reason: resource.DeferredReason(s.deferred.Reason),
}
return
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm interested what the potential impact might be with introducing the automatic deferral after all of these have run:

  • Resource Configure method
  • Provider-defined schema default plan modifiers
  • Null computed attributes marked as unknown
  • Provider-defined schema plan modifiers

I think this should be okay since we don't explicitly pass API client information here, but some of our documentation refers to plan modification as "online", like the blurb about validation: https://developer.hashicorp.com/terraform/plugin/framework/resources/validate-configuration

If we do go with this route, I think we should update the naming/documentation of ResourceBehavior.EnablePlanModification to be more explicit about "what" plan modification logic is being enabled/disabled. Might need to bikeshed a name like EnableModifyPlan, EnableResourcePlanModification, etc 🤔

Any thoughts on this @bflad ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a fair point. Another option is doing similar as the RPCs and returning without doing anything and would technically be the safest choice. I had hoped that since most of the list up there shouldn't be doing anything with using APIs, etc. we could improve the plan output for future rounds, but that could be changed later as an enhancement, rather than the reverse of taking functionality away being a breaking change.

I guess I'll concede we should be safer here and return super early before anything runs. We can always change that decision later. I do worry about the null to unknown logic not running although maybe core will already do something like that in the plan output based on the configuration (something also worth checking with ReadDataSource).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For safe routes, would it be a bad idea to just duplicate the "marked computed nils as unknown" logic after checking for a deferral at the top of PlanResourceChange?

modifiedPlan, err := tftypes.Transform(resp.PlannedState.Raw, MarkComputedNilsAsUnknown(ctx, req.Config.Raw, req.ResourceSchema))

I'm also cool with just coming back later and enhancing it once we have more info, using ProposedNewState like SDKv2: https://github.com/hashicorp/terraform-plugin-sdk/blob/5fee97c1b78c352a1195194a12a9ad514508e3c6/helper/schema/grpc_provider.go#L777-L792

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably fine but I guess it might be good to evaluate the "current" behavior if the provider side just reflects that data without adding the extra logic as maybe its not needed. That evaluation does not need to happen immediately though, which is nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a flag in 31f33f8 to essentially skip everything except "Null computed attributes marked as unknown" logic. Unfortunately, there's a lot of prerequisites to calling MarkComputedNilsAsUnknown() that I felt was bloating the deferral check block, but if the flag logic is too complicated, I can go back to duplicating the logic in the deferral check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only reservation in adding a flag to all the other pieces of plan logic is just future maintenance/changes to PlanResourceChange, i.e. it's easier to describe just the operations that automatic deferred plans needs to run, rather than describe all the operations (and future operations) that shouldn't run.

If there are a ton of operations and such that need to run for the computed marking, maybe we should just return early with ProposedNewState (like SDKv2 is doing) for now and treat it as an enhancement once deferred actions/stacks is more in-use.

Plans with deferred actions aren't held to the consistency rules by Terraform between rounds, so this is mostly a UX problem

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense, I've decided to return early with the ProposedNewState and we can circle back on this issue later.

Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on this, @SBGoods 👍 A few notes, but overall pretty close. Will let @austinvalle give the final ✅

@@ -378,6 +424,11 @@ func TestServerImportResourceState(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()

if testCase.configureProviderReq != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice solution here 👍

internal/fwserver/server_planresourcechange.go Outdated Show resolved Hide resolved
internal/fwserver/server_readdatasource.go Outdated Show resolved Hide resolved
internal/fwserver/server_configureprovider.go Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to terraform-plugin-sdk, we should include a changelog note talking about deferral being experimental 👍

https://github.com/hashicorp/terraform-plugin-sdk/releases/tag/v2.34.0 for inspiration

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are planning on releasing this with the manual deferred action work, this should be covered by: https://github.com/hashicorp/terraform-plugin-framework/blob/main/.changes/unreleased/NOTES-20240510-143136.yaml

Comment on lines 254 to 262
// Skip resource-level ModifyPlan for automatic deferrals
// unless ProviderDeferredBehavior.EnablePlanModification is true
if s.deferred != nil && !req.ResourceBehavior.ProviderDeferred.EnablePlanModification {
logging.FrameworkDebug(ctx, "Provider has deferred response configured, automatically returning deferred response.")
resp.Deferred = &resource.Deferred{
Reason: resource.DeferredReason(s.deferred.Reason),
}
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a fair point. Another option is doing similar as the RPCs and returning without doing anything and would technically be the safest choice. I had hoped that since most of the list up there shouldn't be doing anything with using APIs, etc. we could improve the plan output for future rounds, but that could be changed later as an enhancement, rather than the reverse of taking functionality away being a breaking change.

I guess I'll concede we should be safer here and return super early before anything runs. We can always change that decision later. I do worry about the null to unknown logic not running although maybe core will already do something like that in the plan output based on the configuration (something also worth checking with ReadDataSource).

@bflad bflad added this to the v1.9.0 milestone May 22, 2024
@bflad bflad added the enhancement New feature or request label May 22, 2024
@SBGoods SBGoods requested review from bflad and austinvalle May 23, 2024 22:26
Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just need to update the PlanResourceChange unit testing to cover deferred reason overrides, otherwise nice work and looks good to me 🚀

internal/fwserver/server_configureprovider.go Outdated Show resolved Hide resolved
@SBGoods SBGoods removed the request for review from austinvalle May 30, 2024 22:26
@SBGoods SBGoods merged commit 60c2c5a into main May 30, 2024
28 checks passed
@SBGoods SBGoods deleted the SBGoods/automatic-deferred-action-support branch May 30, 2024 22:31
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 30, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants