mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: add prebuilds system user (#16916)
Pre-requisite for https://github.com/coder/coder/pull/16891 Closes https://github.com/coder/internal/issues/515 This PR introduces a new concept of a "system" user. Our data model requires that all workspaces have an owner (a `users` relation), and prebuilds is a feature that will spin up workspaces to be claimed later by actual users - and thus needs to own the workspaces in the interim. Naturally, introducing a change like this touches a few aspects around the codebase and we've taken the approach _default hidden_ here; in other words, queries for users will by default _exclude_ all system users, but there is a flag to ensure they can be displayed. This keeps the changeset relatively small. This user has minimal permissions (it's equivalent to a `member` since it has no roles). It will be associated with the default org in the initial migration, and thereafter we'll need to somehow ensure its membership aligns with templates (which are org-scoped) for which it'll need to provision prebuilds; that's a solution we'll have in a subsequent PR. --------- Signed-off-by: Danny Kopping <dannykopping@gmail.com> Co-authored-by: Sas Swart <sas.swart.cdk@gmail.com>
This commit is contained in:
co-authored by
Sas Swart
parent
117e4c2fe7
commit
4c33846f6d
@@ -23,6 +23,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/notifications/types"
|
||||
"github.com/coder/coder/v2/coderd/prebuilds"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
@@ -154,6 +155,22 @@ func New() database.Store {
|
||||
panic(xerrors.Errorf("failed to create psk provisioner key: %w", err))
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
// We can't insert this user using the interface, because it's a system user.
|
||||
q.data.users = append(q.data.users, database.User{
|
||||
ID: prebuilds.SystemUserID,
|
||||
Email: "prebuilds@coder.com",
|
||||
Username: "prebuilds",
|
||||
CreatedAt: dbtime.Now(),
|
||||
UpdatedAt: dbtime.Now(),
|
||||
Status: "active",
|
||||
LoginType: "none",
|
||||
HashedPassword: []byte{},
|
||||
IsSystem: true,
|
||||
Deleted: false,
|
||||
})
|
||||
q.mutex.Unlock()
|
||||
|
||||
return q
|
||||
}
|
||||
|
||||
@@ -442,6 +459,7 @@ func convertUsers(users []database.User, count int64) []database.GetUsersRow {
|
||||
Deleted: u.Deleted,
|
||||
LastSeenAt: u.LastSeenAt,
|
||||
Count: count,
|
||||
IsSystem: u.IsSystem,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1554,11 +1572,16 @@ func (q *FakeQuerier) ActivityBumpWorkspace(ctx context.Context, arg database.Ac
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) AllUserIDs(_ context.Context) ([]uuid.UUID, error) {
|
||||
// nolint:revive // It's not a control flag, it's a filter.
|
||||
func (q *FakeQuerier) AllUserIDs(_ context.Context, includeSystem bool) ([]uuid.UUID, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
userIDs := make([]uuid.UUID, 0, len(q.users))
|
||||
for idx := range q.users {
|
||||
if !includeSystem && q.users[idx].IsSystem {
|
||||
continue
|
||||
}
|
||||
|
||||
userIDs = append(userIDs, q.users[idx].ID)
|
||||
}
|
||||
return userIDs, nil
|
||||
@@ -2649,12 +2672,17 @@ func (q *FakeQuerier) GetAPIKeysLastUsedAfter(_ context.Context, after time.Time
|
||||
return apiKeys, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetActiveUserCount(_ context.Context) (int64, error) {
|
||||
// nolint:revive // It's not a control flag, it's a filter.
|
||||
func (q *FakeQuerier) GetActiveUserCount(_ context.Context, includeSystem bool) (int64, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
active := int64(0)
|
||||
for _, u := range q.users {
|
||||
if !includeSystem && u.IsSystem {
|
||||
continue
|
||||
}
|
||||
|
||||
if u.Status == database.UserStatusActive && !u.Deleted {
|
||||
active++
|
||||
}
|
||||
@@ -3390,7 +3418,8 @@ func (q *FakeQuerier) GetGroupByOrgAndName(_ context.Context, arg database.GetGr
|
||||
return database.Group{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetGroupMembers(ctx context.Context) ([]database.GroupMember, error) {
|
||||
//nolint:revive // It's not a control flag, its a filter
|
||||
func (q *FakeQuerier) GetGroupMembers(ctx context.Context, includeSystem bool) ([]database.GroupMember, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
@@ -3398,6 +3427,9 @@ func (q *FakeQuerier) GetGroupMembers(ctx context.Context) ([]database.GroupMemb
|
||||
members = append(members, q.groupMembers...)
|
||||
for _, org := range q.organizations {
|
||||
for _, user := range q.users {
|
||||
if !includeSystem && user.IsSystem {
|
||||
continue
|
||||
}
|
||||
members = append(members, database.GroupMemberTable{
|
||||
UserID: user.ID,
|
||||
GroupID: org.ID,
|
||||
@@ -3420,17 +3452,17 @@ func (q *FakeQuerier) GetGroupMembers(ctx context.Context) ([]database.GroupMemb
|
||||
return groupMembers, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetGroupMembersByGroupID(ctx context.Context, id uuid.UUID) ([]database.GroupMember, error) {
|
||||
func (q *FakeQuerier) GetGroupMembersByGroupID(ctx context.Context, arg database.GetGroupMembersByGroupIDParams) ([]database.GroupMember, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
if q.isEveryoneGroup(id) {
|
||||
return q.getEveryoneGroupMembersNoLock(ctx, id), nil
|
||||
if q.isEveryoneGroup(arg.GroupID) {
|
||||
return q.getEveryoneGroupMembersNoLock(ctx, arg.GroupID), nil
|
||||
}
|
||||
|
||||
var groupMembers []database.GroupMember
|
||||
for _, member := range q.groupMembers {
|
||||
if member.GroupID == id {
|
||||
if member.GroupID == arg.GroupID {
|
||||
groupMember, err := q.getGroupMemberNoLock(ctx, member.UserID, member.GroupID)
|
||||
if errors.Is(err, errUserDeleted) {
|
||||
continue
|
||||
@@ -3445,8 +3477,8 @@ func (q *FakeQuerier) GetGroupMembersByGroupID(ctx context.Context, id uuid.UUID
|
||||
return groupMembers, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetGroupMembersCountByGroupID(ctx context.Context, groupID uuid.UUID) (int64, error) {
|
||||
users, err := q.GetGroupMembersByGroupID(ctx, groupID)
|
||||
func (q *FakeQuerier) GetGroupMembersCountByGroupID(ctx context.Context, arg database.GetGroupMembersCountByGroupIDParams) (int64, error) {
|
||||
users, err := q.GetGroupMembersByGroupID(ctx, database.GetGroupMembersByGroupIDParams(arg))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -6223,12 +6255,16 @@ func (q *FakeQuerier) GetUserByID(_ context.Context, id uuid.UUID) (database.Use
|
||||
return q.getUserByIDNoLock(id)
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetUserCount(_ context.Context) (int64, error) {
|
||||
// nolint:revive // It's not a control flag, it's a filter.
|
||||
func (q *FakeQuerier) GetUserCount(_ context.Context, includeSystem bool) (int64, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
existing := int64(0)
|
||||
for _, u := range q.users {
|
||||
if !includeSystem && u.IsSystem {
|
||||
continue
|
||||
}
|
||||
if !u.Deleted {
|
||||
existing++
|
||||
}
|
||||
@@ -6580,6 +6616,12 @@ func (q *FakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams
|
||||
users = usersFilteredByLastSeen
|
||||
}
|
||||
|
||||
if !params.IncludeSystem {
|
||||
users = slices.DeleteFunc(users, func(u database.User) bool {
|
||||
return u.IsSystem
|
||||
})
|
||||
}
|
||||
|
||||
if params.GithubComUserID != 0 {
|
||||
usersFilteredByGithubComUserID := make([]database.User, 0, len(users))
|
||||
for i, user := range users {
|
||||
@@ -8933,6 +8975,7 @@ func (q *FakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParam
|
||||
Status: status,
|
||||
RBACRoles: arg.RBACRoles,
|
||||
LoginType: arg.LoginType,
|
||||
IsSystem: false,
|
||||
}
|
||||
q.users = append(q.users, user)
|
||||
sort.Slice(q.users, func(i, j int) bool {
|
||||
@@ -10091,7 +10134,7 @@ func (q *FakeQuerier) UpdateInactiveUsersToDormant(_ context.Context, params dat
|
||||
|
||||
var updated []database.UpdateInactiveUsersToDormantRow
|
||||
for index, user := range q.users {
|
||||
if user.Status == database.UserStatusActive && user.LastSeenAt.Before(params.LastSeenAfter) {
|
||||
if user.Status == database.UserStatusActive && user.LastSeenAt.Before(params.LastSeenAfter) && !user.IsSystem {
|
||||
q.users[index].Status = database.UserStatusDormant
|
||||
q.users[index].UpdatedAt = params.UpdatedAt
|
||||
updated = append(updated, database.UpdateInactiveUsersToDormantRow{
|
||||
|
||||
Reference in New Issue
Block a user