generated from hashicorp/terraform-provider-scaffolding
-
Notifications
You must be signed in to change notification settings - Fork 50
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
HCPF-1961: Implement retry mechanism for Get and Set Policy #870
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
05337ea
Create custom diagnostics that stores the error code
delores-hashicorp 605927d
Return custom diagnostics
delores-hashicorp ed9abab
Retry read-modify-write when conflict occurs during policy update
delores-hashicorp 1a45dff
Fix go imports
delores-hashicorp eb44bb3
Expect http error code instead of grpc
delores-hashicorp 7c1fc09
Add changelog
delores-hashicorp b52f446
Cast to the correct error
delores-hashicorp 4b5874d
Add comments and update how the retry works
delores-hashicorp 255db12
Refactor custom error diag
delores-hashicorp 92491f4
Update documentation
delores-hashicorp d2d2e97
Update documentation
delores-hashicorp c7d9a4c
Update groups iam policy resource to use custom diag
delores-hashicorp 820fd47
Merge branch 'main' into bugfix/retry-get-policy
delores-hashicorp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:bug | ||
Fix intermittent conflicts during IAM policy updates | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,13 +5,15 @@ package iampolicy | |
|
||
import ( | ||
"context" | ||
"log" | ||
"sync" | ||
"time" | ||
|
||
iamModels "github.com/hashicorp/hcp-sdk-go/clients/cloud-iam/stable/2019-12-10/models" | ||
"github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/stable/2019-12-10/models" | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-provider-hcp/internal/clients" | ||
"github.com/hashicorp/terraform-provider-hcp/internal/customdiags" | ||
"golang.org/x/exp/maps" | ||
) | ||
|
||
|
@@ -184,45 +186,72 @@ func (f *policyFuture) executeModifers(ctx context.Context, u ResourceIamUpdater | |
return | ||
} | ||
|
||
// Get the existing policy | ||
ep, diags := u.GetResourceIamPolicy(ctx) | ||
if diags.HasError() { | ||
f.set(nil, diags) | ||
return | ||
} | ||
backoff := time.Second | ||
maxBackoff := 30 * time.Second | ||
|
||
// Determine the principal's we need to lookup their type. | ||
principalSet, diags := f.getPrincipals(ctx, client) | ||
if diags.HasError() { | ||
f.set(nil, diags) | ||
return | ||
} | ||
for { | ||
// Get the existing policy | ||
ep, diags := u.GetResourceIamPolicy(ctx) | ||
if diags.HasError() { | ||
f.set(nil, diags) | ||
return | ||
} | ||
|
||
// Remove any bindings needed | ||
bindings := ToMap(ep) | ||
for _, rm := range f.removers { | ||
if members, ok := bindings[rm.RoleID]; ok { | ||
delete(members, rm.Members[0].MemberID) | ||
if len(members) == 0 { | ||
delete(bindings, rm.RoleID) | ||
} | ||
// Determine the principal's we need to lookup their type. | ||
principalSet, diags := f.getPrincipals(ctx, client) | ||
if diags.HasError() { | ||
f.set(nil, diags) | ||
return | ||
} | ||
} | ||
|
||
// Go through the setters and apply them | ||
for _, s := range f.setters { | ||
members, ok := bindings[s.RoleID] | ||
if !ok { | ||
members = make(map[string]*models.HashicorpCloudResourcemanagerPolicyBindingMemberType, 4) | ||
bindings[s.RoleID] = members | ||
// Remove any bindings needed | ||
bindings := ToMap(ep) | ||
for _, rm := range f.removers { | ||
if members, ok := bindings[rm.RoleID]; ok { | ||
delete(members, rm.Members[0].MemberID) | ||
if len(members) == 0 { | ||
delete(bindings, rm.RoleID) | ||
} | ||
} | ||
} | ||
|
||
members[s.Members[0].MemberID] = principalSet[s.Members[0].MemberID] | ||
} | ||
// Go through the setters and apply them | ||
for _, s := range f.setters { | ||
members, ok := bindings[s.RoleID] | ||
if !ok { | ||
members = make(map[string]*models.HashicorpCloudResourcemanagerPolicyBindingMemberType, 4) | ||
bindings[s.RoleID] = members | ||
} | ||
|
||
// Apply the policy | ||
f.set(u.SetResourceIamPolicy(ctx, FromMap(ep.Etag, bindings))) | ||
members[s.Members[0].MemberID] = principalSet[s.Members[0].MemberID] | ||
} | ||
|
||
// Apply the policy | ||
ep, diags = u.SetResourceIamPolicy(ctx, FromMap(ep.Etag, bindings)) | ||
if diags.HasError() { | ||
if customdiags.HasConflictError(diags) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a comment here explaining that the policy object has changed and the etag is no longer valid and how the retry works. |
||
// Policy object has changed since it was last gotten and the etag is now different. | ||
// Continuously retry getting and setting the policy with an increasing backoff period until the maximum backoff period is reached. | ||
if backoff > maxBackoff { | ||
log.Printf("[DEBUG]: Maximum backoff time reached. Aborting operation.") | ||
f.set(nil, diags) | ||
return | ||
} | ||
log.Printf("[DEBUG]: Operation failed due to conflicts. Operation will be restarted after %s", backoff) | ||
// Pause the execution for the duration specified by the current backoff time. | ||
time.Sleep(backoff) | ||
// Double the backoff time to increase the delay for the next retry. | ||
backoff *= 2 | ||
continue | ||
} | ||
f.set(nil, diags) | ||
return | ||
} | ||
|
||
// Successfully applied policy | ||
f.set(ep, nil) | ||
return | ||
} | ||
} | ||
|
||
// getPrincipals returns a map of principal_id to binding type. The binding type | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package customdiags | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
) | ||
|
||
// ErrorHTTPStatusCode is an error diagnostic that stored the error code. | ||
type ErrorHTTPStatusCode struct { | ||
detail string | ||
summary string | ||
HTTPStatusCode int | ||
} | ||
|
||
// Detail returns the diagnostic detail. | ||
func (d ErrorHTTPStatusCode) Detail() string { | ||
return d.detail | ||
} | ||
|
||
// Equal returns true if the other diagnostic is equivalent. | ||
func (d ErrorHTTPStatusCode) Equal(o diag.Diagnostic) bool { | ||
ed, ok := o.(ErrorHTTPStatusCode) | ||
|
||
if !ok { | ||
return false | ||
} | ||
|
||
return ed.Summary() == d.Summary() && ed.Detail() == d.Detail() && ed.HTTPStatusCode == d.HTTPStatusCode | ||
} | ||
|
||
// Summary returns the diagnostic summary. | ||
func (d ErrorHTTPStatusCode) Summary() string { | ||
return d.summary | ||
} | ||
|
||
// Severity returns the diagnostic severity. | ||
func (d ErrorHTTPStatusCode) Severity() diag.Severity { | ||
return diag.SeverityError | ||
} | ||
|
||
// NewErrorHTTPStatusCode returns a new error severity diagnostic with the given summary, detail and error code. | ||
func NewErrorHTTPStatusCode(summary string, detail string, statusCode int) ErrorHTTPStatusCode { | ||
return ErrorHTTPStatusCode{ | ||
detail: detail, | ||
summary: summary, | ||
HTTPStatusCode: statusCode, | ||
} | ||
} | ||
|
||
// HasConflictError checks if a diagnostic has a specific error code. | ||
func HasConflictError(diags diag.Diagnostics) bool { | ||
for _, d := range diags { | ||
diag, ok := d.(*ErrorHTTPStatusCode) | ||
if !ok { | ||
return false | ||
} | ||
if diag.HTTPStatusCode == http.StatusConflict { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for cleaning up the comments ✨