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

Refer to Terraform diff to generate a more precise diff #1499

Closed
wants to merge 3 commits into from

Conversation

guineveresaenger
Copy link
Contributor

This pull request looks at the Terraform Diff Attributes, checks if they exist in the Pulumi diff generated by olds and news, and adds any additional diff information to Pulumi diff.

Points of concern:

  • This change is not yet under test; suggestions or help modeling a schema map and schema info are welcome.
  • How detailed we can make the property diff on each schema path? There's room for improvement here.

Addresses #1491.

@iwahbe iwahbe self-requested a review November 1, 2023 00:48
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.

This will need to be under test to merge. To start, I would write table-driven unit tests for parseTFPath. You can use shim/schema.* to mock shim.Schema* interfaces:

//nolint:revive
type SchemaMap map[string]shim.Schema

}
// reset the schema map to point at the nested schema
if nestedSchema != nil {
if res, isres := nestedSchema.Elem().(shim.Resource); isres {
Copy link
Member

Choose a reason for hiding this comment

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

If !isres, then tfs should be set to nil. A misalignment doesn't mean that no traversal happened.

Also, it's not actually a resource:

Suggested change
if res, isres := nestedSchema.Elem().(shim.Resource); isres {
if res, isObj := nestedSchema.Elem().(shim.Resource); isObj {

Comment on lines +343 to +344
nestedSchema, found := tfs.GetOk(field)
if found {
Copy link
Member

Choose a reason for hiding this comment

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

Tightening the scoping makes it harder to misuse these variables.

Suggested change
nestedSchema, found := tfs.GetOk(field)
if found {
if nestedSchema, found := tfs.GetOk(field); found {

Comment on lines +358 to +366
} else {
// check if the field is a number
// the error value for strconv.Atoi is 0, so we look at the error.
_, err := strconv.Atoi(field) // the error value here is 0, so we look at the error.
if err == nil {
// we found an index
pulumiPath = pulumiPath + "[" + field + "]"
} else {
// Ignore special characters from Terraform
Copy link
Member

Choose a reason for hiding this comment

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

I think this would read clearer if we reduce the nesting.

Suggested change
} else {
// check if the field is a number
// the error value for strconv.Atoi is 0, so we look at the error.
_, err := strconv.Atoi(field) // the error value here is 0, so we look at the error.
if err == nil {
// we found an index
pulumiPath = pulumiPath + "[" + field + "]"
} else {
// Ignore special characters from Terraform
continue
}
// check if the field is a number
// the error value for strconv.Atoi is 0, so we look at the error.
_, err := strconv.Atoi(field) // the error value here is 0, so we look at the error.
if err == nil {
// we found an index
pulumiPath = pulumiPath + "[" + field + "]"
continue
}
// Ignore special characters from Terraform

Comment on lines +322 to +323
// TODO: Using PropertyDiff_ADD is not entirely accurate in every case.
// TODO: (cont) We may want to pass on a more generic UPDATE strategy here - less precise, less error prone.
Copy link
Member

Choose a reason for hiding this comment

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

Can you give an example for the case there PropertyDiff_ADD is the wrong case? What is the effect of getting the kind wrong (is it just display)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could be a PropertyDiff_UPDATE, on an already-existing object key.

@t0yv0 t0yv0 self-requested a review November 1, 2023 15:59
Copy link
Member

@t0yv0 t0yv0 left a comment

Choose a reason for hiding this comment

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

Thank you for looking into this!

Making changes to this code is currently an extremely high-risk area, and existing coverage is very inadequate to verify changes. If you're contributing to the expectation is to have 100% coverage on changes but also back-filling missing coverage.

While it is possible to continue with the chosen approach and get there, it is going to be require some work.

Can we discuss alternative approaches that avoid high-risk edits? Perhaps we can start with a repro of the original problem?

changes := pulumirpc.DiffResponse_DIFF_NONE
if len(diff) > 0 || *forceDiff {
changes = pulumirpc.DiffResponse_DIFF_SOME
}
return diff, changes
}

func parseTFPath(path string, tfs shim.SchemaMap, ps map[string]*SchemaInfo) (string, bool) {
Copy link
Member

Choose a reason for hiding this comment

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

Do we understand the semantics of paths here, both the TF paths and Pulumi paths?

What happens with set elements? (represented as hashes in TF paths). Should these be mapped to Pulumi numbers as steps are represented as lists?

What happens with flattening (when Pulumi paths are shorter than TF paths with MaxItems=1).

diff[pulumiPath] = &pulumirpc.PropertyDiff{Kind: pulumirpc.PropertyDiff_UPDATE}
}
}
}
changes := pulumirpc.DiffResponse_DIFF_NONE
Copy link
Member

Choose a reason for hiding this comment

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

What would happen if we considered changing the changes only without populating diff[pulumiPath] at all? Is there a way such an approach could get us to passing the bug, possibly with incorrect detailed diff displayed to the user?

@t0yv0
Copy link
Member

t0yv0 commented Nov 1, 2023

Linking related issue #1498

@@ -309,9 +309,71 @@ func makeDetailedDiff(
makePropertyDiff(ctx, en, string(k), v, tfDiff, diff, forceDiff, etf, eps, true, useRawNames(etf))
}

// In some cases, the terraform diff will have additional attributes not covered by Pulumi's olds and news.
Copy link
Member

Choose a reason for hiding this comment

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

For my education can you spell it out as a full example for the GCP problem. I understand this is related to GCP provider-level labels, what's the attribute name and is it possible to show a printout of tfDiff in question. Much appreciated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here is a tfDiff.Attributes() when adding the new GCP labels:

           "effective_labels.%": {
               Old:         "3",
               New:         "4",
               NewComputed: false,
               NewRemoved:  false,
               NewExtra:    nil,
               RequiresNew: false,
               Sensitive:   false,
               Type:        0x0,
           },
           "effective_labels.new": {
               Old:         "",
               New:         "defaultLabel",
               NewComputed: false,
               NewRemoved:  false,
               NewExtra:    nil,
               RequiresNew: false,
               Sensitive:   false,
               Type:        0x0,
           },
           "terraform_labels.%": {
               Old:         "3",
               New:         "4",
               NewComputed: false,
               NewRemoved:  false,
               NewExtra:    nil,
               RequiresNew: false,
               Sensitive:   false,
               Type:        0x0,
           },
           "terraform_labels.new": {
               Old:         "",
               New:         "defaultLabel",
               NewComputed: false,
               NewRemoved:  false,
               NewExtra:    nil,
               RequiresNew: false,
               Sensitive:   false,
               Type:        0x0,
           },
       }

@EvanBoyle
Copy link
Contributor

Should we close this given that we merged #1502?

@t0yv0
Copy link
Member

t0yv0 commented Nov 3, 2023

I have filed #1504 detailing remaining work to consider here, and why we should consider finishing this up. I will close for now. I think there's a substantial amount of work required to get this right; the code in this PR will still be linked to 1504 if we need to find it again.

@t0yv0 t0yv0 closed this Nov 3, 2023
@guineveresaenger guineveresaenger deleted the guin/labels-diff branch February 23, 2024 21:42
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.

4 participants