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

Fix set marshaling. #104

Merged
merged 2 commits into from
Jan 24, 2018
Merged

Fix set marshaling. #104

merged 2 commits into from
Jan 24, 2018

Conversation

pgavlin
Copy link
Member

@pgavlin pgavlin commented Jan 24, 2018

With the recent change to use provider diffs in Update, we ceased to
produce usable inputs for sets. The break involves the specific
representation of set members in a terraform.InstanceState value: each
set member is flattened into a set of key-value pairs under the key
prefix.<hash-of-member>.*. When we were constructing the diffs
ourselves, we were able to ensure that each <hash-of-member> component
lined up betweemn the old state and the new config (note that this was
itself perhaps subtly wrong, as TF would likely have considered a change
to a set member to have changed its hash and thus its identity; most
changes of this sort in TF appear as a set member removal and a set member
addition). We lost this capability when we moved to TF diffs: when using
TF diffs, the TF state presented to diff must use TF's typical encoding
for set elements.

These changes reimplement old state -> TF state marshaling s.t. set
elements are flattened with the correct prefix. This turned out to be
much more challenging than it ought to be, as TF does not directly
expose the APIs used to compute set member hashes. At first, I attempted
to use a MapFieldWriter to construct the flat map, but this particular
type appears to have some problems handling TypeMap values (namely, it
expects each field of a TypeMap value to be represented as a
map[string]interface{}, which is clearly incorrect). Instead, I chose
to use the TF set member hashing functions directly, which required two
irritating adjustments:

  • These hash functions expect any set elements to be represented using
    whatever types would have been produced by a FieldReader. As a
    result, the state goes through the following transforms:
    grpc.Struct -> pulumi.PropertyMap -> TF inputs -> TF config
    The resulting TF config is then fed into a ConfigFieldReader. The
    top-level values are read out and flattened in order to produce the TF
    attributes.
  • These has functions do not produce the exact hash as used by TF: if
    the value they produce is negative, TF will invert it s.t. it is
    always positive. We do the same.

With these changes, I am able to update a CloudFront distribution's
caching rules (which involves set marshaling) without issue.

@pgavlin pgavlin requested review from mmdriley and joeduffy January 24, 2018 03:49
@pgavlin
Copy link
Member Author

pgavlin commented Jan 24, 2018

It appears that this also fixed #99.

@pgavlin
Copy link
Member Author

pgavlin commented Jan 24, 2018

Note that I am not 100% confident in this change. I will be testing this locally by building a new AWS provider with these changes and running the pulumi-{aws,cloud} integration tests.

@pgavlin pgavlin force-pushed the FixSet branch 2 times, most recently from 76123f1 to 405e9d5 Compare January 24, 2018 04:15
@joeduffy
Copy link
Member

Great, I was just about to suggest that. I took a look but will review in depth in the AM. 💤

@pgavlin
Copy link
Member Author

pgavlin commented Jan 24, 2018

pulumi-aws tests have passed successfully.

@pgavlin
Copy link
Member Author

pgavlin commented Jan 24, 2018

The pulumi-cloud tests I've been able to run have run successfully. This includes the containers example. I ran into resource limits with the cluster and countdown examples; I am rerunning those now.

@joeduffy
Copy link
Member

LGTM. Can you add a basic test for this too? (I'm fine merging first.)

@@ -391,9 +393,78 @@ func MakeTerraformAttributesFromRPC(res *PulumiResource, m *pbstruct.Struct,
return MakeTerraformAttributes(res, props, tfs, ps, defaults)
}

func flattenValue(result map[string]string, prefix string, value interface{}) {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe a quick // flattenValue ... comment here, describing this function's purpose and its logic?

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do.

Copy link
Contributor

Choose a reason for hiding this comment

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

also a reference to the corresponding implementation in Terraform we're trying to replicate.

@pgavlin
Copy link
Member Author

pgavlin commented Jan 24, 2018

pulumi-cloud tests passed 👍

@joeduffy
Copy link
Member

👍

if ps != nil {
eps = ps.Elem
}

Copy link
Contributor

Choose a reason for hiding this comment

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

We hoisted this because it's invariant, right? I.e. this wasn't a change in behavior?

Copy link
Member Author

Choose a reason for hiding this comment

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

Correct. I can undo this if you'd like; it isn't necessary for this fix. It is an artifact of an earlier attempt.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's take it with this PR but split it into a different commit.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@mmdriley mmdriley left a comment

Choose a reason for hiding this comment

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

lgtm

With the recent change to use provider diffs in `Update`, we ceased to
produce usable inputs for sets. The break involves the specific
representation of set members in a `terraform.InstanceState` value: each
set member is flattened into a set of key-value pairs under the key
`prefix.<hash-of-member>.*`. When we were constructing the diffs
ourselves, we were able to ensure that each `<hash-of-member>` component
lined up betweemn the old state and the new config (note that this was
itself perhaps subtly wrong, as TF would likely have considered a change
to a set member to have changed its hash and thus its identity; most
changes of this sort in TF appear as a set member removal and a set member
addition). We lost this capability when we moved to TF diffs: when using
TF diffs, the TF state presented to diff must use TF's typical encoding
for set elements.

These changes reimplement old state -> TF state marshaling s.t. set
elements are flattened with the correct prefix. This turned out to be
much more challenging than it ought to be, as TF does not directly
expose the APIs used to compute set member hashes. At first, I attempted
to use a `MapFieldWriter` to construct the flat map, but this particular
type appears to have some problems handling `TypeMap` values (namely, it
expects each field of a `TypeMap` value to be represented as a
`map[string]interface{}`, which is clearly incorrect). Instead, I chose
to use the TF set member hashing functions directly, which required two
irritating adjustments:
- These hash functions expect any set elements to be represented using
  whatever types would have been produced by a `FieldReader`. As a
  result, the state goes through the following transforms:
    grpc.Struct -> pulumi.PropertyMap -> TF inputs -> TF config
  The resulting TF config is then fed into a `ConfigFieldReader`. The
  top-level values are read out and flattened in order to produce the TF
  attributes.
- These has functions do not produce the exact hash as used by TF: if
  the value they produce is negative, TF will invert it s.t. it is
  always positive. We do the same.

With these changes, I am able to update a CloudFront distribution's
caching rules (which involves set marshaling) without issue.
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.

3 participants