Skip to content

Commit a359879

Browse files
authored
chore: scope workspace quotas to organizations (coder#14352)
* chore: scope workspace quotas to organizations Quotas are now a function of (user_id, organization_id). They are still sourced from groups. Deprecate the old api endpoint.
1 parent fa73331 commit a359879

File tree

16 files changed

+309
-68
lines changed

16 files changed

+309
-68
lines changed

coderd/apidoc/docs.go

Lines changed: 45 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 41 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbauthz/dbauthz.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,20 +1823,20 @@ func (q *querier) GetProvisionerLogsAfterID(ctx context.Context, arg database.Ge
18231823
return q.db.GetProvisionerLogsAfterID(ctx, arg)
18241824
}
18251825

1826-
func (q *querier) GetQuotaAllowanceForUser(ctx context.Context, userID uuid.UUID) (int64, error) {
1827-
err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceUserObject(userID))
1826+
func (q *querier) GetQuotaAllowanceForUser(ctx context.Context, params database.GetQuotaAllowanceForUserParams) (int64, error) {
1827+
err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceUserObject(params.UserID))
18281828
if err != nil {
18291829
return -1, err
18301830
}
1831-
return q.db.GetQuotaAllowanceForUser(ctx, userID)
1831+
return q.db.GetQuotaAllowanceForUser(ctx, params)
18321832
}
18331833

1834-
func (q *querier) GetQuotaConsumedForUser(ctx context.Context, userID uuid.UUID) (int64, error) {
1835-
err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceUserObject(userID))
1834+
func (q *querier) GetQuotaConsumedForUser(ctx context.Context, params database.GetQuotaConsumedForUserParams) (int64, error) {
1835+
err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceUserObject(params.OwnerID))
18361836
if err != nil {
18371837
return -1, err
18381838
}
1839-
return q.db.GetQuotaConsumedForUser(ctx, userID)
1839+
return q.db.GetQuotaConsumedForUser(ctx, params)
18401840
}
18411841

18421842
func (q *querier) GetReplicaByID(ctx context.Context, id uuid.UUID) (database.Replica, error) {

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,11 +1075,17 @@ func (s *MethodTestSuite) TestUser() {
10751075
}))
10761076
s.Run("GetQuotaAllowanceForUser", s.Subtest(func(db database.Store, check *expects) {
10771077
u := dbgen.User(s.T(), db, database.User{})
1078-
check.Args(u.ID).Asserts(u, policy.ActionRead).Returns(int64(0))
1078+
check.Args(database.GetQuotaAllowanceForUserParams{
1079+
UserID: u.ID,
1080+
OrganizationID: uuid.New(),
1081+
}).Asserts(u, policy.ActionRead).Returns(int64(0))
10791082
}))
10801083
s.Run("GetQuotaConsumedForUser", s.Subtest(func(db database.Store, check *expects) {
10811084
u := dbgen.User(s.T(), db, database.User{})
1082-
check.Args(u.ID).Asserts(u, policy.ActionRead).Returns(int64(0))
1085+
check.Args(database.GetQuotaConsumedForUserParams{
1086+
OwnerID: u.ID,
1087+
OrganizationID: uuid.New(),
1088+
}).Asserts(u, policy.ActionRead).Returns(int64(0))
10831089
}))
10841090
s.Run("GetUserByEmailOrUsername", s.Subtest(func(db database.Store, check *expects) {
10851091
u := dbgen.User(s.T(), db, database.User{})

coderd/database/dbmem/dbmem.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3314,13 +3314,13 @@ func (q *FakeQuerier) GetProvisionerLogsAfterID(_ context.Context, arg database.
33143314
return logs, nil
33153315
}
33163316

3317-
func (q *FakeQuerier) GetQuotaAllowanceForUser(_ context.Context, userID uuid.UUID) (int64, error) {
3317+
func (q *FakeQuerier) GetQuotaAllowanceForUser(_ context.Context, params database.GetQuotaAllowanceForUserParams) (int64, error) {
33183318
q.mutex.RLock()
33193319
defer q.mutex.RUnlock()
33203320

33213321
var sum int64
33223322
for _, member := range q.groupMembers {
3323-
if member.UserID != userID {
3323+
if member.UserID != params.UserID {
33243324
continue
33253325
}
33263326
if _, err := q.getOrganizationByIDNoLock(member.GroupID); err == nil {
@@ -3340,27 +3340,33 @@ func (q *FakeQuerier) GetQuotaAllowanceForUser(_ context.Context, userID uuid.UU
33403340
// Grab the quota for the Everyone group iff the user is a member of
33413341
// said organization.
33423342
for _, mem := range q.organizationMembers {
3343-
if mem.UserID != userID {
3343+
if mem.UserID != params.UserID {
33443344
continue
33453345
}
33463346

33473347
group, err := q.getGroupByIDNoLock(context.Background(), mem.OrganizationID)
33483348
if err != nil {
33493349
return -1, xerrors.Errorf("failed to get everyone group for org %q", mem.OrganizationID.String())
33503350
}
3351+
if group.OrganizationID != params.OrganizationID {
3352+
continue
3353+
}
33513354
sum += int64(group.QuotaAllowance)
33523355
}
33533356

33543357
return sum, nil
33553358
}
33563359

3357-
func (q *FakeQuerier) GetQuotaConsumedForUser(_ context.Context, userID uuid.UUID) (int64, error) {
3360+
func (q *FakeQuerier) GetQuotaConsumedForUser(_ context.Context, params database.GetQuotaConsumedForUserParams) (int64, error) {
33583361
q.mutex.RLock()
33593362
defer q.mutex.RUnlock()
33603363

33613364
var sum int64
33623365
for _, workspace := range q.workspaces {
3363-
if workspace.OwnerID != userID {
3366+
if workspace.OwnerID != params.OwnerID {
3367+
continue
3368+
}
3369+
if workspace.OrganizationID != params.OrganizationID {
33643370
continue
33653371
}
33663372
if workspace.Deleted {

coderd/database/dbmetrics/dbmetrics.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbmock/dbmock.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,10 @@ func TestWorkspaceQuotas(t *testing.T) {
604604
db2sdk.List([]database.OrganizationMember{memOne, memTwo}, orgMemberIDs))
605605

606606
// Check the quota is correct.
607-
allowance, err := db.GetQuotaAllowanceForUser(ctx, one.ID)
607+
allowance, err := db.GetQuotaAllowanceForUser(ctx, database.GetQuotaAllowanceForUserParams{
608+
UserID: one.ID,
609+
OrganizationID: org.ID,
610+
})
608611
require.NoError(t, err)
609612
require.Equal(t, int64(50), allowance)
610613

@@ -617,7 +620,10 @@ func TestWorkspaceQuotas(t *testing.T) {
617620
require.NoError(t, err)
618621

619622
// Ensure allowance remains the same
620-
allowance, err = db.GetQuotaAllowanceForUser(ctx, one.ID)
623+
allowance, err = db.GetQuotaAllowanceForUser(ctx, database.GetQuotaAllowanceForUserParams{
624+
UserID: one.ID,
625+
OrganizationID: org.ID,
626+
})
621627
require.NoError(t, err)
622628
require.Equal(t, int64(50), allowance)
623629
})

coderd/database/queries.sql.go

Lines changed: 22 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/quotas.sql

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ FROM
55
(
66
-- Select all groups this user is a member of. This will also include
77
-- the "Everyone" group for organizations the user is a member of.
8-
SELECT * FROM group_members_expanded WHERE @user_id = user_id
8+
SELECT * FROM group_members_expanded
9+
WHERE
10+
@user_id = user_id AND
11+
@organization_id = group_members_expanded.organization_id
912
) AS members
1013
INNER JOIN groups ON
1114
members.group_id = groups.id
@@ -30,4 +33,8 @@ FROM
3033
workspaces
3134
JOIN latest_builds ON
3235
latest_builds.workspace_id = workspaces.id
33-
WHERE NOT deleted AND workspaces.owner_id = $1;
36+
WHERE NOT
37+
deleted AND
38+
workspaces.owner_id = @owner_id AND
39+
workspaces.organization_id = @organization_id
40+
;

codersdk/workspaces.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,8 @@ type WorkspaceQuota struct {
572572
Budget int `json:"budget"`
573573
}
574574

575-
func (c *Client) WorkspaceQuota(ctx context.Context, userID string) (WorkspaceQuota, error) {
576-
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspace-quota/%s", userID), nil)
575+
func (c *Client) WorkspaceQuota(ctx context.Context, organizationID string, userID string) (WorkspaceQuota, error) {
576+
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/members/%s/workspace-quota", organizationID, userID), nil)
577577
if err != nil {
578578
return WorkspaceQuota{}, err
579579
}

0 commit comments

Comments
 (0)