Skip to content

Commit 918935d

Browse files
committed
fix(iam): handle http 409 transient error
* exponentialy retry Add amd Remove group member requests
1 parent a7ee6d7 commit 918935d

File tree

1 file changed

+62
-4
lines changed

1 file changed

+62
-4
lines changed

internal/services/iam/group_membership.go

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import (
44
"context"
55
"fmt"
66
"strings"
7+
"time"
78

89
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
910
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1011
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
1112
"github.com/scaleway/scaleway-sdk-go/scw"
1213
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/transport"
1315
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
1416
)
1517

@@ -53,11 +55,11 @@ func resourceIamGroupMembershipCreate(ctx context.Context, d *schema.ResourceDat
5355
userID := types.ExpandStringPtr(d.Get("user_id"))
5456
applicationID := types.ExpandStringPtr(d.Get("application_id"))
5557

56-
group, err := api.AddGroupMember(&iam.AddGroupMemberRequest{
58+
group, err := MakeGroupRequest(ctx, api, &iam.AddGroupMemberRequest{
5759
GroupID: d.Get("group_id").(string),
5860
UserID: userID,
5961
ApplicationID: applicationID,
60-
}, scw.WithContext(ctx))
62+
})
6163
if err != nil {
6264
return diag.FromErr(err)
6365
}
@@ -74,7 +76,7 @@ func resourceIamGroupMembershipRead(ctx context.Context, d *schema.ResourceData,
7476
if err != nil {
7577
return diag.FromErr(err)
7678
}
77-
79+
// http GET request should not return a 409 error
7880
group, err := api.GetGroup(&iam.GetGroupRequest{
7981
GroupID: groupID,
8082
}, scw.WithContext(ctx))
@@ -139,7 +141,7 @@ func resourceIamGroupMembershipDelete(ctx context.Context, d *schema.ResourceDat
139141
req.ApplicationID = &applicationID
140142
}
141143

142-
_, err = api.RemoveGroupMember(req, scw.WithContext(ctx))
144+
_, err = MakeGroupRequest(ctx, api, req)
143145
if err != nil {
144146
if httperrors.Is404(err) {
145147
d.SetId("")
@@ -178,3 +180,59 @@ func ExpandGroupMembershipID(id string) (groupID string, userID string, applicat
178180

179181
return
180182
}
183+
184+
func MakeGroupRequest(ctx context.Context, api *iam.API, request any) (*iam.Group, error) {
185+
retryInterval := 50 * time.Millisecond
186+
maxRetries := 10
187+
188+
if transport.DefaultWaitRetryInterval != nil {
189+
retryInterval = *transport.DefaultWaitRetryInterval
190+
}
191+
192+
switch req := request.(type) {
193+
case *iam.AddGroupMemberRequest:
194+
for i := range maxRetries {
195+
response, err := api.AddGroupMember(req, scw.WithContext(ctx))
196+
if err != nil {
197+
if handleTransientError(err, req.GroupID, retryInterval, i) {
198+
continue
199+
}
200+
201+
return nil, err
202+
}
203+
204+
return response, nil
205+
}
206+
207+
return nil, fmt.Errorf("failed to add group member after %d retries", maxRetries)
208+
209+
case *iam.RemoveGroupMemberRequest:
210+
for i := range maxRetries {
211+
response, err := api.RemoveGroupMember(req, scw.WithContext(ctx))
212+
if err != nil {
213+
if handleTransientError(err, req.GroupID, retryInterval, i) {
214+
continue
215+
}
216+
217+
return nil, err
218+
}
219+
220+
return response, nil
221+
}
222+
223+
return nil, fmt.Errorf("failed to remove group member after %d retries", maxRetries)
224+
225+
default:
226+
return nil, fmt.Errorf("invalid request type: %T", req)
227+
}
228+
}
229+
230+
func handleTransientError(err error, groupID string, retryInterval time.Duration, maxRetries int) bool {
231+
if httperrors.Is409(err) && strings.Contains(err.Error(), fmt.Sprintf("resource group with ID %s is in a transient state: updating", groupID)) {
232+
time.Sleep(retryInterval * time.Duration(maxRetries)) // lintignore: R018
233+
234+
return true
235+
}
236+
237+
return false
238+
}

0 commit comments

Comments
 (0)