@@ -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