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

Pass PlannedPrivate from PlanResourceChange to ApplyResourceChange #2407

Merged
merged 13 commits into from
Sep 11, 2024

Conversation

flostadler
Copy link
Contributor

@flostadler flostadler commented Sep 9, 2024

The PlannedPrivate field of the PRC response includes metadata that's required for correctly re-assembling the diff.
For example, it includes NewExtra which includes the original value for attributes with a StateFunc.
Without PlannedPrivate, those attributes get saved to state encoded twice. This can lead to mangled data in state and perma-diffs.

Fixes #2408

@flostadler flostadler self-assigned this Sep 9, 2024
Copy link

codecov bot commented Sep 9, 2024

Codecov Report

Attention: Patch coverage is 90.47619% with 2 lines in your changes missing coverage. Please review.

Project coverage is 57.11%. Comparing base (fddce46) to head (1e1ac96).
Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
pkg/tfshim/sdk-v2/provider2.go 90.47% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2407   +/-   ##
=======================================
  Coverage   57.10%   57.11%           
=======================================
  Files         369      369           
  Lines       50593    50604   +11     
=======================================
+ Hits        28891    28902   +11     
  Misses      20124    20124           
  Partials     1578     1578           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

The `PlannedPrivate` field of the PRC response includes metadata
that's required for correctly re-assembling the diff.
For example, it includes `NewExtra` which includes the original
value for attributes with a `StateFunc`.
Without `PlannedPrivate`, those attributes get saved to state
encoded twice. This can lead to mangled data in state and
perma-diffs.
@flostadler flostadler force-pushed the flostadler/prc-statefunc-bug branch from 852d21f to b807281 Compare September 10, 2024 11:25
@flostadler flostadler changed the title PRC StateFunc applied twice repro Pass PlannedPrivate from PlanResourceChange to ApplyResourceChange Sep 10, 2024
@@ -5113,7 +5113,8 @@ func TestPlanResourceChangeUnknowns(t *testing.T) {
"setBlockProps":[],
"listBlockProps":[],
"nestedListBlockProps":[],
"maxItemsOneBlockProp":null
"maxItemsOneBlockProp":null,
"__meta":"{\"_new_extra_shim\":{}}"
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 wonder if this will cause pain somewhere else? I.e. re-recording of upgrade tests?

Copy link
Contributor

Choose a reason for hiding this comment

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

We'll likely have to fix a bunch of GRPC tests everywhere but that's fine.

@flostadler flostadler marked this pull request as ready for review September 10, 2024 13:31
PlannedMeta map[string]interface{}
PlannedDiff *terraform.InstanceDiff
PlannedState cty.Value
PlannedPrivate map[string]interface{}
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 is actually PlannedPrivate not PlannedMeta. Renaming this so it aligns better with terraform

@VenelinMartinov
Copy link
Contributor

Is the third parameter on CreateContext/UpdateContext the PlannedPrivate in TF? If so, I think we can write cross-tests here similar to

func TestInputsEqualStringBasic(t *testing.T) {
to make sure that what we pass to the TF provider matches what TF passes to the TF provider.

@flostadler
Copy link
Contributor Author

Is the third parameter on CreateContext/UpdateContext the PlannedPrivate in TF? If so, I think we can write cross-tests here similar to

func TestInputsEqualStringBasic(t *testing.T) {

to make sure that what we pass to the TF provider matches what TF passes to the TF provider.

Sadly it's not :(

Copy link
Member

@iwahbe iwahbe left a comment

Choose a reason for hiding this comment

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

It's hard for me to review, just because it's hard to know what the correct behavior is.

I'd leave this for @VenelinMartinov and @t0yv0 unless I need to spin up on this.

@VenelinMartinov
Copy link
Contributor

Sadly it's not :(

Where is the PlannedPrivate picked up on the TF side? Is there a way for us to hook into that and write a cross-test for that? That'd give us more confidence that we are matching the TF behaviour here.

Don't want to block the PR on this since the changes look sensible but it'd be nice if we can be confident here.

@VenelinMartinov
Copy link
Contributor

How does the TF provider access the PlannedPrivate?

@t0yv0
Copy link
Member

t0yv0 commented Sep 10, 2024

I was working with this on Florian a bit this morning, I can review. It looked safe enough but we were exploring how it's tangled to timeouts and there's a little bit of residual uncertainty I have around how TF handles timeout etc encoding in PlannedPrivate as well as whether we're doing a good job of mapping planned private to Pulumi protocol. It's a bit subtle.

@flostadler
Copy link
Contributor Author

@VenelinMartinov
What we're interested in is this bit here: https://github.com/hashicorp/terraform-plugin-sdk/blob/527cfe4ccf1738d4efcfd6006bbd2068f4a35a06/helper/schema/grpc_provider.go#L1111-L1122

It's reconstructing the NewExtra field of the ResourceAttrDiff using data in PlannedPrivate.
NewExtra contains the original value the attribute was set to before the StateFunc was applied (even though the name NewExtra is misleading).

The resource access methods (e.g. schema.ResourceData::GetOk) either return the value in state or NewExtra if it's set.
Right now the regression test is asserting what's stored in state.
Can we craft a cross-test out of this?

@VenelinMartinov
Copy link
Contributor

Maybe not without bigger changes - this property seems to be hidden from the provider too and only accessible from within the plugin-sdk. The provider only sees the .Get result change depending on if this is set or not.

Is this correct? If so, then we'd need to add a hook in the plugin-sdk for this which is certainly out of scope here.

@flostadler
Copy link
Contributor Author

@VenelinMartinov Yeah you're understanding is correct. It's hidden in the ResourceData and the .Get result changes are the only thing providers see.

@t0yv0
Copy link
Member

t0yv0 commented Sep 10, 2024

Cross-test would be very helpful if just to debug the TF side of things.

@iwahbe
Copy link
Member

iwahbe commented Sep 11, 2024

Why couldn't we write a cross test that asserts on the results of .Get? We don't need a cross-test to assert the internals, only the provider visible section.

@VenelinMartinov
Copy link
Contributor

Yeah, that bit is possible - a test like

func TestInputsEqualStringBasic(t *testing.T) {
but with the StateFunc and the modifications as in the existing test.

Unfortunately that wouldn't cover the full contents of the PlannedPrivate field.

@flostadler
Copy link
Contributor Author

@t0yv0 @VenelinMartinov I added a cross test, please have a look

Copy link
Contributor

@VenelinMartinov VenelinMartinov left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for digging in here!

@flostadler flostadler merged commit 7864bb0 into master Sep 11, 2024
11 checks passed
@flostadler flostadler deleted the flostadler/prc-statefunc-bug branch September 11, 2024 15:00
@mjeffryes mjeffryes added this to the 0.110 milestone Sep 12, 2024
@pulumi-bot
Copy link
Contributor

This PR has been shipped in release v3.91.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

StateFunc applied twice for resources under PRC
6 participants