-
Notifications
You must be signed in to change notification settings - Fork 894
chore: support multi-org group sync with runtime configuration #14578
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
Changes from 34 commits
99c97c2
bfddeb6
f2857c6
791a059
4326e9d
6d3ed2e
0803619
596e7b4
b9476ac
ee8e4e4
d5ff0f7
86c0f6f
2f03e18
ec8092d
d63727d
2a1769c
640e86e
c544a29
476be45
164aeac
986498d
290cfa5
c563b10
d2c247f
12685bd
bf0d4ed
f95128e
88b0ad9
6491f6a
bd23288
a390ec4
a0a1c53
a86ba83
0df7f28
7a802a9
611f1e3
7f28a53
41994d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package coderdtest | ||
|
||
import "github.com/google/uuid" | ||
|
||
// DeterministicUUIDGenerator allows "naming" uuids for unit tests. | ||
// An example of where this is useful, is when a tabled test references | ||
// a UUID that is not yet known. An alternative to this would be to | ||
// hard code some UUID strings, but these strings are not human friendly. | ||
type DeterministicUUIDGenerator struct { | ||
Named map[string]uuid.UUID | ||
} | ||
|
||
func NewDeterministicUUIDGenerator() *DeterministicUUIDGenerator { | ||
return &DeterministicUUIDGenerator{ | ||
Named: make(map[string]uuid.UUID), | ||
} | ||
} | ||
|
||
func (d *DeterministicUUIDGenerator) ID(name string) uuid.UUID { | ||
if v, ok := d.Named[name]; ok { | ||
return v | ||
} | ||
d.Named[name] = uuid.New() | ||
return d.Named[name] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package coderdtest_test | ||
|
||
import ( | ||
"github.com/google/uuid" | ||
|
||
"github.com/coder/coder/v2/coderd/coderdtest" | ||
) | ||
|
||
func ExampleNewDeterministicUUIDGenerator() { | ||
det := coderdtest.NewDeterministicUUIDGenerator() | ||
testCases := []struct { | ||
CreateUsers []uuid.UUID | ||
ExpectedIDs []uuid.UUID | ||
}{ | ||
{ | ||
CreateUsers: []uuid.UUID{ | ||
det.ID("player1"), | ||
det.ID("player2"), | ||
}, | ||
ExpectedIDs: []uuid.UUID{ | ||
det.ID("player1"), | ||
det.ID("player2"), | ||
}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
tc := tc | ||
_ = tc | ||
// Do the test with CreateUsers as the setup, and the expected IDs | ||
// will match. | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -682,6 +682,17 @@ func (q *FakeQuerier) getWorkspaceResourcesByJobIDNoLock(_ context.Context, jobI | |
return resources, nil | ||
} | ||
|
||
func (q *FakeQuerier) getGroupByNameNoLock(arg database.NameOrganizationPair) (database.Group, error) { | ||
for _, group := range q.groups { | ||
if group.OrganizationID == arg.OrganizationID && | ||
group.Name == arg.Name { | ||
return group, nil | ||
} | ||
} | ||
|
||
return database.Group{}, sql.ErrNoRows | ||
} | ||
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. I'm very curious what this is necessary for 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. Oh, I originally was using it in another query. Wanted to reuse the same code. Now it's not necessary to extract to it's own function 🤷♂️. I'll put it back. |
||
|
||
func (q *FakeQuerier) getGroupByIDNoLock(_ context.Context, id uuid.UUID) (database.Group, error) { | ||
for _, group := range q.groups { | ||
if group.ID == id { | ||
|
@@ -2613,14 +2624,10 @@ func (q *FakeQuerier) GetGroupByOrgAndName(_ context.Context, arg database.GetGr | |
q.mutex.RLock() | ||
defer q.mutex.RUnlock() | ||
|
||
for _, group := range q.groups { | ||
if group.OrganizationID == arg.OrganizationID && | ||
group.Name == arg.Name { | ||
return group, nil | ||
} | ||
} | ||
|
||
return database.Group{}, sql.ErrNoRows | ||
return q.getGroupByNameNoLock(database.NameOrganizationPair{ | ||
Name: arg.Name, | ||
OrganizationID: arg.OrganizationID, | ||
}) | ||
} | ||
|
||
func (q *FakeQuerier) GetGroupMembers(ctx context.Context) ([]database.GroupMember, error) { | ||
|
@@ -2695,18 +2702,18 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) | |
q.mutex.RLock() | ||
defer q.mutex.RUnlock() | ||
|
||
groupIDs := make(map[uuid.UUID]struct{}) | ||
userGroupIDs := make(map[uuid.UUID]struct{}) | ||
if arg.HasMemberID != uuid.Nil { | ||
for _, member := range q.groupMembers { | ||
if member.UserID == arg.HasMemberID { | ||
groupIDs[member.GroupID] = struct{}{} | ||
userGroupIDs[member.GroupID] = struct{}{} | ||
} | ||
} | ||
|
||
// Handle the everyone group | ||
for _, orgMember := range q.organizationMembers { | ||
if orgMember.UserID == arg.HasMemberID { | ||
groupIDs[orgMember.OrganizationID] = struct{}{} | ||
userGroupIDs[orgMember.OrganizationID] = struct{}{} | ||
} | ||
} | ||
} | ||
|
@@ -2718,11 +2725,15 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) | |
continue | ||
} | ||
|
||
_, ok := groupIDs[group.ID] | ||
_, ok := userGroupIDs[group.ID] | ||
if arg.HasMemberID != uuid.Nil && !ok { | ||
continue | ||
} | ||
|
||
if len(arg.GroupNames) > 0 && !slices.Contains(arg.GroupNames, group.Name) { | ||
continue | ||
} | ||
|
||
orgDetails, ok := orgDetailsCache[group.ID] | ||
if !ok { | ||
for _, org := range q.organizations { | ||
|
@@ -7015,7 +7026,37 @@ func (q *FakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParam | |
return user, nil | ||
} | ||
|
||
func (q *FakeQuerier) InsertUserGroupsByID(_ context.Context, arg database.InsertUserGroupsByIDParams) ([]uuid.UUID, error) { | ||
err := validateDatabaseType(arg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
q.mutex.Lock() | ||
defer q.mutex.Unlock() | ||
|
||
var groupIDs []uuid.UUID | ||
for _, group := range q.groups { | ||
for _, groupID := range arg.GroupIds { | ||
if group.ID == groupID { | ||
q.groupMembers = append(q.groupMembers, database.GroupMemberTable{ | ||
UserID: arg.UserID, | ||
GroupID: groupID, | ||
}) | ||
groupIDs = append(groupIDs, group.ID) | ||
} | ||
} | ||
} | ||
|
||
return groupIDs, nil | ||
} | ||
|
||
func (q *FakeQuerier) InsertUserGroupsByName(_ context.Context, arg database.InsertUserGroupsByNameParams) error { | ||
err := validateDatabaseType(arg) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
q.mutex.Lock() | ||
defer q.mutex.Unlock() | ||
|
||
|
@@ -7607,6 +7648,34 @@ func (q *FakeQuerier) RemoveUserFromAllGroups(_ context.Context, userID uuid.UUI | |
return nil | ||
} | ||
|
||
func (q *FakeQuerier) RemoveUserFromGroups(_ context.Context, arg database.RemoveUserFromGroupsParams) ([]uuid.UUID, error) { | ||
err := validateDatabaseType(arg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
q.mutex.Lock() | ||
defer q.mutex.Unlock() | ||
|
||
removed := make([]uuid.UUID, 0) | ||
q.data.groupMembers = slices.DeleteFunc(q.data.groupMembers, func(groupMember database.GroupMemberTable) bool { | ||
// Delete all group members that match the arguments. | ||
if groupMember.UserID != arg.UserID { | ||
// Not the right user, ignore. | ||
return false | ||
} | ||
|
||
if !slices.Contains(arg.GroupIds, groupMember.GroupID) { | ||
return false | ||
} | ||
|
||
removed = append(removed, groupMember.GroupID) | ||
return true | ||
}) | ||
|
||
return removed, nil | ||
} | ||
|
||
func (q *FakeQuerier) RevokeDBCryptKey(_ context.Context, activeKeyDigest string) error { | ||
q.mutex.Lock() | ||
defer q.mutex.Unlock() | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.