Skip to content

Commit 4815b45

Browse files
committed
wip
1 parent eb1453a commit 4815b45

File tree

5 files changed

+3114
-444
lines changed

5 files changed

+3114
-444
lines changed

internal/services/iam/group_membership.go

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66
"slices"
7-
"sort"
87
"strings"
98
"time"
109

@@ -34,28 +33,28 @@ func ResourceGroupMembership() *schema.Resource {
3433
},
3534
SchemaVersion: 0,
3635
Schema: map[string]*schema.Schema{
36+
"group_id": {
37+
Type: schema.TypeString,
38+
Required: true,
39+
Description: "The ID of the group to add the users or applications to",
40+
ForceNew: true,
41+
},
3742
"user_ids": {
3843
Type: schema.TypeList,
3944
Elem: &schema.Schema{Type: schema.TypeString},
4045
Optional: true,
41-
ExactlyOneOf: []string{"application_ids"},
42-
Description: "The IDs of the users",
46+
Description: "The IDs of the users to add to the group",
47+
AtLeastOneOf: []string{"application_ids"},
4348
ForceNew: true,
4449
},
4550
"application_ids": {
4651
Type: schema.TypeList,
4752
Elem: &schema.Schema{Type: schema.TypeString},
4853
Optional: true,
49-
ExactlyOneOf: []string{"user_ids"},
50-
Description: "The IDs of the applications",
54+
Description: "The IDs of the applications to add to the group",
55+
AtLeastOneOf: []string{"user_ids"},
5156
ForceNew: true,
5257
},
53-
"group_id": {
54-
Type: schema.TypeString,
55-
Required: true,
56-
Description: "The ID of the group to add the user to",
57-
ForceNew: true,
58-
},
5958
},
6059
}
6160
}
@@ -83,7 +82,7 @@ func resourceIamGroupMembershipCreate(ctx context.Context, d *schema.ResourceDat
8382
func resourceIamGroupMembershipRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
8483
api := NewAPI(m)
8584

86-
groupID, entityKind, entityIDs, err := ExpandGroupMembershipResourceID(d.Id())
85+
groupID, entityIDs, err := ExpandGroupMembershipResourceID(d.Id())
8786
if err != nil {
8887
return diag.FromErr(err)
8988
}
@@ -101,46 +100,43 @@ func resourceIamGroupMembershipRead(ctx context.Context, d *schema.ResourceData,
101100
return diag.FromErr(err)
102101
}
103102

104-
foundEntityIDs := make([]bool, len(entityIDs))
103+
foundUserIDs := make([]bool, len(entityIDs[EntityKindUser]))
104+
foundApplicationIDs := make([]bool, len(entityIDs[EntityKindApplication]))
105105

106-
if entityKind == EntityKindUser {
107-
for i, groupUserID := range group.UserIDs {
108-
if slices.Contains(entityIDs, groupUserID) {
109-
foundEntityIDs[i] = true
110-
}
111-
}
112-
} else if entityKind == EntityKindApplication {
113-
for i, groupApplicationID := range group.ApplicationIDs {
114-
if slices.Contains(entityIDs, groupApplicationID) {
115-
foundEntityIDs[i] = true
116-
}
106+
for i, userID := range entityIDs[EntityKindUser] {
107+
if slices.Contains(group.UserIDs, userID) {
108+
foundUserIDs[i] = true
109+
} else {
110+
return diag.FromErr(fmt.Errorf("user %s not found in group %s", userID, groupID))
117111
}
118112
}
119113

120-
if slices.Contains(foundEntityIDs, false) {
121-
d.SetId("")
122-
123-
return nil
114+
for i, applicationID := range entityIDs[EntityKindApplication] {
115+
if slices.Contains(group.ApplicationIDs, applicationID) {
116+
foundApplicationIDs[i] = true
117+
} else {
118+
return diag.FromErr(fmt.Errorf("application %s not found in group %s", applicationID, groupID))
119+
}
124120
}
125121

126122
_ = d.Set("group_id", groupID)
127-
_ = d.Set(fmt.Sprintf("%s_ids", entityKind), entityIDs)
123+
_ = d.Set("user_ids", entityIDs[EntityKindUser])
124+
_ = d.Set("application_ids", entityIDs[EntityKindApplication])
128125

129126
return nil
130127
}
131128

132129
func resourceIamGroupMembershipDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
133130
api := NewAPI(m)
134131

135-
groupID, _, _, err := ExpandGroupMembershipResourceID(d.Id())
132+
groupID, _, err := ExpandGroupMembershipResourceID(d.Id())
136133
if err != nil {
137134
return diag.FromErr(err)
138135
}
139136

140137
_, err = MakeSetGroupMembershipRequest(ctx, api, &iam.SetGroupMembersRequest{
141138
GroupID: groupID,
142139
})
143-
144140
if err != nil {
145141
if httperrors.Is404(err) {
146142
d.SetId("")
@@ -154,31 +150,49 @@ func resourceIamGroupMembershipDelete(ctx context.Context, d *schema.ResourceDat
154150
return nil
155151
}
156152

153+
// Depending on the kind of entity, it will generate a parsable state like:
154+
// groupID/user:userID,application:applicationID
157155
func SetGroupMembershipResourceID(groupID string, userIDs []string, applicationIDs []string) (resourceID string) {
158-
sort.Strings(userIDs)
159-
sort.Strings(applicationIDs)
156+
entityIDs := make([]string, 0)
157+
158+
for _, userID := range userIDs {
159+
entityIDs = append(entityIDs, fmt.Sprintf("%s:%s", EntityKindUser, userID))
160+
}
160161

161-
if len(userIDs) > 0 {
162-
resourceID = fmt.Sprintf("%s/%s/%s", groupID, EntityKindUser, strings.Join(userIDs, ","))
163-
} else if len(applicationIDs) > 0 {
164-
resourceID = fmt.Sprintf("%s/%s/%s", groupID, EntityKindApplication, strings.Join(applicationIDs, ","))
162+
for _, applicationID := range applicationIDs {
163+
entityIDs = append(entityIDs, fmt.Sprintf("%s:%s", EntityKindApplication, applicationID))
165164
}
166165

166+
resourceID = fmt.Sprintf("%s/%s", groupID, strings.Join(entityIDs, ","))
167+
167168
return
168169
}
169170

170-
func ExpandGroupMembershipResourceID(id string) (groupID string, kind EntityKind, entityIDs []string, err error) {
171+
func ExpandGroupMembershipResourceID(id string) (groupID string, entityIDs map[EntityKind][]string, err error) {
171172
elems := strings.Split(id, "/")
172-
if len(elems) != 3 {
173-
return "", "", []string{}, fmt.Errorf("invalid group membership id format, expected {groupID}/{entityKind}/{entityIDs}, got: %s", id)
173+
if len(elems) != 2 {
174+
return "", nil, fmt.Errorf("invalid group membership id format, expected {groupID}/{entityKind}:{entityIDs}, got: %s", id)
174175
}
175176

176177
groupID = elems[0]
177-
kind = EntityKind(elems[1])
178-
if kind != EntityKindUser && kind != EntityKindApplication {
179-
return "", "", []string{}, fmt.Errorf("invalid entity kind, expected %s or %s, got: %s", EntityKindUser, EntityKindApplication, kind)
178+
179+
// entityKind:entityID,entityKind:entityID
180+
entityKindAndIDs := strings.Split(elems[1], ",")
181+
entityIDs = make(map[EntityKind][]string)
182+
183+
for _, entityKindAndID := range entityKindAndIDs {
184+
splitted := strings.Split(entityKindAndID, ":")
185+
if len(splitted) != 2 {
186+
return "", nil, fmt.Errorf("invalid entity kind and id format, expected {entityKind}:{entityID}, got: %s", entityKindAndID)
187+
}
188+
189+
entityKind, entityID := EntityKind(splitted[0]), splitted[1]
190+
if entityKind != EntityKindUser && entityKind != EntityKindApplication {
191+
return "", nil, fmt.Errorf("invalid entity kind, expected %s or %s, got: %s", EntityKindUser, EntityKindApplication, entityKind)
192+
}
193+
194+
entityIDs[entityKind] = append(entityIDs[entityKind], entityID)
180195
}
181-
entityIDs = strings.Split(elems[2], ",")
182196

183197
return
184198
}
@@ -191,15 +205,14 @@ func MakeSetGroupMembershipRequest(ctx context.Context, api *iam.API, request *i
191205
retryInterval = *transport.DefaultWaitRetryInterval
192206
}
193207

194-
var response *iam.Group
195-
var err error
196-
197208
// exponential backoff
198-
for i := 0; i < maxRetries; i++ {
199-
response, err = api.SetGroupMembers(request, scw.WithContext(ctx))
209+
for i := range maxRetries {
210+
response, err := api.SetGroupMembers(request, scw.WithContext(ctx))
211+
200212
if err != nil {
201213
if httperrors.Is409(err) && strings.Contains(err.Error(), fmt.Sprintf("resource group with ID %s is in a transient state: updating", request.GroupID)) {
202214
time.Sleep(retryInterval * time.Duration(i))
215+
203216
continue
204217
}
205218

0 commit comments

Comments
 (0)