- Notifications
You must be signed in to change notification settings - Fork1.1k
feat(enterprise/coderd): allow system users to be added to groups#19518
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
6b3a7314b6382690b9078af7c7cd1cd68e34c0c4d493c46e9b3728d3efdfe9f8204ec292760d1471c29ef9a5fbb959c89e1da8e34cf6ee1cb685245e828366adf7657820cfd6File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.
Uh oh!
There was an error while loading.Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -12,6 +12,11 @@ import ( | ||
| "github.com/coder/quartz" | ||
| ) | ||
| const ( | ||
| PrebuiltWorkspacesGroupName = "coderprebuiltworkspaces" | ||
ssncferreira marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| PrebuiltWorkspacesGroupDisplayName = "Prebuilt Workspaces" | ||
| ) | ||
| // StoreMembershipReconciler encapsulates the responsibility of ensuring that the prebuilds system user is a member of all | ||
| // organizations for which prebuilt workspaces are requested. This is necessary because our data model requires that such | ||
| // prebuilt workspaces belong to a member of the organization of their eventual claimant. | ||
| @@ -27,11 +32,16 @@ func NewStoreMembershipReconciler(store database.Store, clock quartz.Clock) Stor | ||
| } | ||
| } | ||
| // ReconcileAll compares the current organization and group memberships of a user to the memberships required | ||
| // in order to create prebuilt workspaces. If the user in question is not yet a member of an organization that | ||
| // needs prebuilt workspaces, ReconcileAll will create the membership required. | ||
| // | ||
| // To facilitate quota management, ReconcileAll will ensure: | ||
| // * the existence of a group (defined by PrebuiltWorkspacesGroupName) in each organization that needs prebuilt workspaces | ||
| // * that the prebuilds system user belongs to the group in each organization that needs prebuilt workspaces | ||
| // * that the group has a quota of 0 by default, which users can adjust based on their needs. | ||
| // | ||
| //ReconcileAll does not have an opinion on transaction or lock management. These responsibilities are left to the caller. | ||
| func (s StoreMembershipReconciler) ReconcileAll(ctx context.Context, userID uuid.UUID, presets []database.GetTemplatePresetsWithPrebuildsRow) error { | ||
| organizationMemberships, err := s.store.GetOrganizationsByUserID(ctx, database.GetOrganizationsByUserIDParams{ | ||
| UserID: userID, | ||
| @@ -44,37 +54,80 @@ func (s StoreMembershipReconciler) ReconcileAll(ctx context.Context, userID uuid | ||
| return xerrors.Errorf("determine prebuild organization membership: %w", err) | ||
| } | ||
| orgMemberships := make(map[uuid.UUID]struct{}, 0) | ||
| defaultOrg, err := s.store.GetDefaultOrganization(ctx) | ||
| if err != nil { | ||
| return xerrors.Errorf("get default organization: %w", err) | ||
| } | ||
| orgMemberships[defaultOrg.ID] = struct{}{} | ||
| for _, o := range organizationMemberships { | ||
| orgMemberships[o.ID] = struct{}{} | ||
| } | ||
| var membershipInsertionErrors error | ||
| for _, preset := range presets { | ||
| _, alreadyOrgMember := orgMemberships[preset.OrganizationID] | ||
| if !alreadyOrgMember { | ||
| // Add the organization to our list of memberships regardless of potential failure below | ||
| // to avoid a retry that will probably be doomed anyway. | ||
| orgMemberships[preset.OrganizationID] = struct{}{} | ||
| // Insert the missing membership | ||
| _, err = s.store.InsertOrganizationMember(ctx, database.InsertOrganizationMemberParams{ | ||
| OrganizationID: preset.OrganizationID, | ||
| UserID: userID, | ||
| CreatedAt: s.clock.Now(), | ||
| UpdatedAt: s.clock.Now(), | ||
| Roles: []string{}, | ||
| }) | ||
| if err != nil { | ||
| membershipInsertionErrors = errors.Join(membershipInsertionErrors, xerrors.Errorf("insert membership for prebuilt workspaces: %w", err)) | ||
| continue | ||
| } | ||
| } | ||
| // determine whether the org already has a prebuilds group | ||
| prebuildsGroupExists := true | ||
| prebuildsGroup, err := s.store.GetGroupByOrgAndName(ctx, database.GetGroupByOrgAndNameParams{ | ||
| OrganizationID: preset.OrganizationID, | ||
| Name: PrebuiltWorkspacesGroupName, | ||
| }) | ||
| if err != nil { | ||
| if !xerrors.Is(err, sql.ErrNoRows) { | ||
| membershipInsertionErrors = errors.Join(membershipInsertionErrors, xerrors.Errorf("get prebuilds group: %w", err)) | ||
| continue | ||
| } | ||
| prebuildsGroupExists = false | ||
| } | ||
| // if the prebuilds group does not exist, create it | ||
| if !prebuildsGroupExists { | ||
| // create a "prebuilds" group in the organization and add the system user to it | ||
| // this group will have a quota of 0 by default, which users can adjust based on their needs | ||
| prebuildsGroup, err = s.store.InsertGroup(ctx, database.InsertGroupParams{ | ||
| ID: uuid.New(), | ||
| Name: PrebuiltWorkspacesGroupName, | ||
| DisplayName: PrebuiltWorkspacesGroupDisplayName, | ||
| OrganizationID: preset.OrganizationID, | ||
| AvatarURL: "", | ||
| QuotaAllowance: 0, // Default quota of 0, users should set this based on their needs | ||
Contributor 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. Correct me if I'm wrong, but does this mean that once this change is rolled out, no more prebuilt workspaces will be able to be provisioneduntil the Quota Allowance is changed? Or is the prebuilds user still part of the ContributorAuthor 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. the prebuilds user is still a member of the everyone group, so this shouldn't be too much of a breaking change. Contributor
| ||
| }) | ||
| if err != nil { | ||
| membershipInsertionErrors = errors.Join(membershipInsertionErrors, xerrors.Errorf("create prebuilds group: %w", err)) | ||
| continue | ||
| } | ||
| } | ||
| // add the system user to the prebuilds group | ||
| err = s.store.InsertGroupMember(ctx, database.InsertGroupMemberParams{ | ||
| GroupID: prebuildsGroup.ID, | ||
| UserID: userID, | ||
| }) | ||
| if err != nil { | ||
| // ignore unique violation errors as the user might already be in the group | ||
| if !database.IsUniqueViolation(err) { | ||
| membershipInsertionErrors = errors.Join(membershipInsertionErrors, xerrors.Errorf("add system user to prebuilds group: %w", err)) | ||
| } | ||
| } | ||
| } | ||
| return membershipInsertionErrors | ||
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.