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: if unable to calculate local md5 hash use old value of detect_md5hash #12032

Draft
wants to merge 66 commits into
base: main
Choose a base branch
from

Conversation

gurusai-voleti
Copy link
Contributor

@gurusai-voleti gurusai-voleti commented Oct 17, 2024

if unable to calculate local md5 hash use old value of detect_md5hash
Fixes: hashicorp/terraform-provider-google#18618

Release Note Template for Downstream PRs (will be copied)

storage: if unable to calculate local md5 hash use old value of detect_md5hash

@github-actions github-actions bot requested a review from roaks3 October 17, 2024 08:59
Copy link

Hello! I am a robot. Tests will require approval from a repository maintainer to run.

@roaks3, a repository maintainer, has been assigned to review your changes. If you have not received review feedback within 2 business days, please leave a comment on this PR asking them to take a look.

You can help make sure that review is quick by doing a self-review and by running impacted tests locally.

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 1 file changed, 3 insertions(+))
google-beta provider: Diff ( 1 file changed, 3 insertions(+))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 109
Passed tests: 100
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 51 insertions(+))
google-beta provider: Diff ( 2 files changed, 51 insertions(+))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 110
Passed tests: 100
Skipped tests: 9
Affected tests: 1

Click here to see the affected service packages
  • storage

Action taken

Found 1 affected test(s) by replaying old test recordings. Starting RECORDING based on the most recent commit. Click here to see the affected tests
  • TestAccStorageObject_detect_md5hash_nofile

Get to know how VCR tests work

@modular-magician
Copy link
Collaborator

🟢 Tests passed during RECORDING mode:
TestAccStorageObject_detect_md5hash_nofile[Debug log]

🟢 No issues found for passed tests after REPLAYING rerun.


🟢 All tests passed!

View the build log or the debug log for each test

@kautikdk
Copy link
Member

kautikdk commented Oct 17, 2024

Hi @gurusai-voleti, This change appears to be the risky fix as we are setting localMd5Hash = old in DiffSuppressFunc when the file is not present. This will prevent terraform diff generation for google_storage_bucket_object resource when the source file is removed. Eventually it will make unnecessary diff in the next terraform plan.
Setting Hash to previous one also prevents diff for file content change in-between two terraform apply if the files are removed locally and file content is changed in terraform config.
Let me know if I am missing something.

PS: Added test appears to be testing different behavior. I suggest, writing test steps and test configs by referring bug reproductions steps.
cc: @roaks3

@gurusai-voleti
Copy link
Contributor Author

gurusai-voleti commented Oct 18, 2024

Hi @gurusai-voleti, This change appears to be the risky fix as we are setting localMd5Hash = old in DiffSuppressFunc when the file is not present. This will prevent terraform diff generation for google_storage_bucket_object resource when the source file is removed. Eventually it will make unnecessary diff in the next terraform plan. Setting Hash to previous one also prevents diff for file content change in-between two terraform apply if the files are removed locally and file content is changed in terraform config. Let me know if I am missing something.

PS: Added test appears to be testing different behavior. I suggest, writing test steps and test configs by referring bug reproductions steps. cc: @roaks3

with this fix in terraform plan wont show any difference and in terraform apply it will create files and wont do anything else unless there are really infra changes to deploy, this is working as expected without this change also with out file delete.
added test case used the same example which is reported by customer

@kautikdk
Copy link
Member

Hi @gurusai-voleti, This change appears to be the risky fix as we are setting localMd5Hash = old in DiffSuppressFunc when the file is not present. This will prevent terraform diff generation for google_storage_bucket_object resource when the source file is removed. Eventually it will make unnecessary diff in the next terraform plan. Setting Hash to previous one also prevents diff for file content change in-between two terraform apply if the files are removed locally and file content is changed in terraform config. Let me know if I am missing something.
PS: Added test appears to be testing different behavior. I suggest, writing test steps and test configs by referring bug reproductions steps. cc: @roaks3

with this fix in terraform plan wont show any difference and in terraform apply it will create files and wont do anything else unless there are really infra changes to deploy, this is working as expected without this change also with out file delete. added test case used the same example which is reported by customer

But if there is a resource dependency then Ideally customer expects a difference. If the source file is being removed, Ideally there should be some difference, Right?

@gurusai-voleti
Copy link
Contributor Author

gurusai-voleti commented Oct 18, 2024

Hi @gurusai-voleti, This change appears to be the risky fix as we are setting localMd5Hash = old in DiffSuppressFunc when the file is not present. This will prevent terraform diff generation for google_storage_bucket_object resource when the source file is removed. Eventually it will make unnecessary diff in the next terraform plan. Setting Hash to previous one also prevents diff for file content change in-between two terraform apply if the files are removed locally and file content is changed in terraform config. Let me know if I am missing something.
PS: Added test appears to be testing different behavior. I suggest, writing test steps and test configs by referring bug reproductions steps. cc: @roaks3

with this fix in terraform plan wont show any difference and in terraform apply it will create files and wont do anything else unless there are really infra changes to deploy, this is working as expected without this change also with out file delete. added test case used the same example which is reported by customer

But if there is a resource dependency then Ideally customer expects a difference. If the source file is being removed, Ideally there should be some difference, Right?

yes if the file is created through a resource block it will show up in the plan, only thing it will not display in the plan is md5 hash difference with this fix, which is the real problem of the issue between plan and apply, I can modify my test case more

@kautikdk
Copy link
Member

kautikdk commented Oct 18, 2024

Hi @roaks3, Is there any way to handle intermediate value change of attribute during terraform apply operation? I believe, The error is coming because of two different values of same attribute during one apply operation.

@gurusai-voleti can you modify PR description as per this guidelines:https://googlecloudplatform.github.io/magic-modules/contribute/create-pr/#requirements. Currently there is no link between this PR and the issue that it is going to fix.

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 51 insertions(+))
google-beta provider: Diff ( 2 files changed, 51 insertions(+))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 110
Passed tests: 101
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 51 insertions(+))
google-beta provider: Diff ( 2 files changed, 51 insertions(+))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 110
Passed tests: 101
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

Copy link

@roaks3 This PR has been waiting for review for 3 weekdays. Please take a look! Use the label disable-review-reminders to disable these notifications.

@roaks3
Copy link
Contributor

roaks3 commented Oct 22, 2024

Yea, this is relatively complicated. The detect_md5hash solution seems to fundamentally be at odds with Terraform paradigms (see https://developer.hashicorp.com/terraform/plugin/sdkv2/guides/terraform-0.12-compatibility#inaccurate-plans), in that it is explicitly communicating a planned value of different hash to the user, but then expects to change it to the actual hash later. I suspect this worked fine with ForceNew for the ~6 years it has been like this (although I'm not certain exactly why), but when it was changed to be mutable in #10038, I think we broke some assumptions and are now seeing this error.

Per the link, you might have luck working with CustomizeDiff to handle the detect_md5hash value properly, but even that could result in an unintended breaking change.

I would recommend narrowing in on the exact failure case, as it is odd that the source file can be removed and then apply works the first time, but then not on subsequent times. Hopefully there is a specific edge case that can be accounted for.

As mentioned by @kautikdk , I'm a bit concerned about the proposed solution in the PR being too broad, and impacting behavior that is working as intended.

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 51 insertions(+))
google-beta provider: Diff ( 2 files changed, 51 insertions(+))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 110
Passed tests: 101
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

Copy link
Contributor

@roaks3 roaks3 left a comment

Choose a reason for hiding this comment

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

To follow up: I was able to confirm with the team the CustomizeDiff was either unavailable, or rarely used, when this functionality was originally introduced, so that is likely a path we can take to fix this issue and possibly simplify the behavior.

@github-actions github-actions bot requested a review from roaks3 October 24, 2024 05:16
@gurusai-voleti gurusai-voleti marked this pull request as draft February 19, 2025 07:42
@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))
google-beta provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 117
Passed tests: 108
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

name = "%s"
bucket = google_storage_bucket.bucket.name
source = "%s"
source_md5hash = "%s"
Copy link
Contributor

Choose a reason for hiding this comment

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

Providing exact md5 hash value externally is not idiomatic in terraform. This should be calculated within terraform either by using md5(...), using local_file resource attributes or in a similar matter.

Using md5(....) will fail on the files that do change during the apply, so the best way is to use local_file and its content_md5 attribute. This ensures that in the plan stage value is passed as "known after apply" and properly calculated during apply

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))
google-beta provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 117
Passed tests: 108
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))
google-beta provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 119
Passed tests: 110
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

Comment on lines +73 to +74
updatedName := getNewTmpTestFile(t, "tf-test")
updatedDataMd5 := writeFile(updatedName.Name(), []byte("datum"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Change of the filename will serve itself as a signal to recreate the file.

Clue of the issue we trying to solve is, that contents of exciting file change. So I recommend just writing additional content to the same file, which is used in first apply.

}
},
Config: testGoogleStorageBucketsObjectBasic(bucketName, testFile.Name()),
Config: testGoogleStorageBucketsObjectFileMd5(bucketName, updatedName.Name()),
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd love to have the same configuration used in both steps. The issue is not, that the user is changing the contents and hence the issue, but with the same terraform config, but changed contents of the files, the error occurs.

Comment on lines +663 to +675
resource "google_storage_bucket" "bucket" {
name = "%s"
location = "US"
}

resource "google_storage_bucket_object" "object" {
name = "%s"
bucket = google_storage_bucket.bucket.name
source = "%s"
source_md5hash = filemd5("%s")
}
`, bucketName, objectName, sourceFilename, sourceFilename)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This case is good, as it covers the case of file that is managed outside of Terraform code (for example - uploading config file).

I recommend add additional case, that I mentioned in this comment - that is verifying behavior, when content of the file is generated by terraform as a part of apply stage.

Adding such cases will ensure, that this problem will not re-surface, and will give confidence, that issue is indeed fixed

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))
google-beta provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 119
Passed tests: 110
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))
google-beta provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 119
Passed tests: 110
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))
google-beta provider: Diff ( 2 files changed, 42 insertions(+), 35 deletions(-))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 119
Passed tests: 110
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

d.SetNewComputed("md5hash")
d.SetNewComputed("generation")
// if any change in source_md5hash, override change of detect_md5hash
d.ForceNew("detect_md5hash")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kautikdk if any change in source_md5hash, ignore the detect_md5hash diffsuppress func at terraform apply time

Copy link
Member

Choose a reason for hiding this comment

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

Why to set ForceNew only when there is a change in source_md5hash?
We should avoid detect_md5hash even when there is no change in source_md5hash. If user specifies this field then ignore detect_md5hash at all operations.

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 verified few methods, but this is the only way to avoid the DiffSuppressFunc of detect_md5hash field

Copy link
Member

@kautikdk kautikdk Mar 11, 2025

Choose a reason for hiding this comment

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

I don't think it is going to work because setting up it as ForceNew will assign "different hash" to the detect_md5hash attribute and it will replace object.

You can probably avoid DiffSuppressFunc by modify flattener of the field.
You can try something like this in resourceStorageBucketObjectRead,

if v, ok := d.GetOk("source_md5hash"); ok{
		if err := d.Set("source_md5hash", v); err != nil {
			return fmt.Errorf("Error setting source_md5hash: %s", err)
		}
		if err := d.Set("detect_md5hash", d.Get("detect_md5hash")); err != nil {
			return fmt.Errorf("Error setting detect_md5hash: %s", err)
		}
	} else {
		if err := d.Set("detect_md5hash", res.Md5Hash); err != nil {
			return fmt.Errorf("Error setting detect_md5hash: %s", err)
		}
	}

This will keep existing value of detect_md5hash as it is if the source_md5hash is specified which should prevent the error. We can probably document this behavior as well that if the source_md5hash is specified then detect_md5hash can not be used and rely on it.
This is not fully tested approach but worth try!

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 tested its not generating any inconsistent final plan, as forceNew is called in CustomizeDiff it acts as new resource, this completely overrides the diffsuppressfunc and assign the default value "different hash" and once create/update completed in read function detect_md5hash assigned with latest md5hash and same is stored in state file

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 to this concern, especially if the google_storage_bucket_object is set to create_before_destroy through propagation from other resources. Then apply results in no file in the bucket. In-place updates are really the only way to avoid such problems / bugs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes agree this is recreating the object which is not a good approach as inplace updates are preferable

Copy link
Member

@kautikdk kautikdk Mar 11, 2025

Choose a reason for hiding this comment

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

I don't think it is going to work because setting up it as ForceNew will assign "different hash" to the detect_md5hash attribute and it will replace object.

You can probably avoid DiffSuppressFunc by modify flattener of the field. You can try something like this in resourceStorageBucketObjectRead,

if v, ok := d.GetOk("source_md5hash"); ok{
		if err := d.Set("source_md5hash", v); err != nil {
			return fmt.Errorf("Error setting source_md5hash: %s", err)
		}
		if err := d.Set("detect_md5hash", d.Get("detect_md5hash")); err != nil {
			return fmt.Errorf("Error setting detect_md5hash: %s", err)
		}
	} else {
		if err := d.Set("detect_md5hash", res.Md5Hash); err != nil {
			return fmt.Errorf("Error setting detect_md5hash: %s", err)
		}
	}

This will keep existing value of detect_md5hash as it is if the source_md5hash is specified which should prevent the error. We can probably document this behavior as well that if the source_md5hash is specified then detect_md5hash can not be used and rely on it. This is not fully tested approach but worth try!

You can try the suggestion I mentioned in this message. Probably we can avoid DiffSupress mechanism by setting detect_md5hash everytime with the previous value. It will only take effect when user specifies source_md5hash so it won't break any userflows and will handle introduction of source_md5hash as well so that we can gracefully deprecate and remove detect_md5hash field.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in CustomizeDiff there is no chance of setting the value, either setnewcomputed or forcenew is only possible it seems

tried the suggested solution in create/update still facing issue of inconsistent final plan, its not ignoring the diffsuppressfunc of detect_md5hash

Copy link
Member

@kautikdk kautikdk Mar 12, 2025

Choose a reason for hiding this comment

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

Can you please push those changes as well so that we can take a look?

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 44 insertions(+), 35 deletions(-))
google-beta provider: Diff ( 2 files changed, 44 insertions(+), 35 deletions(-))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 119
Passed tests: 110
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

@modular-magician
Copy link
Collaborator

Hi there, I'm the Modular magician. I've detected the following information about your changes:

Diff report

Your PR generated some diffs in downstreams - here they are.

google provider: Diff ( 2 files changed, 43 insertions(+), 43 deletions(-))
google-beta provider: Diff ( 2 files changed, 43 insertions(+), 43 deletions(-))

@modular-magician
Copy link
Collaborator

Tests analytics

Total tests: 119
Passed tests: 110
Skipped tests: 9
Affected tests: 0

Click here to see the affected service packages
  • storage

🟢 All tests passed!

View the build log

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

Successfully merging this pull request may close these issues.

Fix Inconsistent Final Plan issue for Google Storage Bucket Object
6 participants