Make internal bot usernames and emails unique across cells
What does this MR do and why?
Internal bot users get a username and email that are unique across cells.
Problem
On the newly provisioned Cell 9 in staging, the root user got a 500 error right after login gitlab-com/gl-infra/tenant-scale/cells-infrastructure/team#941 (comment 3906607714).
app/controllers/concerns/homepage_data.rb calls Users::Internal.in_organization(org_id).duo_code_review_bot. This call creates the "GitLab Duo" bot user on first use. The bot username GitLabDuo and email gitlab-duo@staging.gitlab.com were only made unique against the local database. lib/users/internal.rb uses Gitlab::Utils::Uniquify with a numeric counter for this.
On a cell that does not own the default organization, the first organization is the per-cell admin organization. Gitlab::DatabaseImporters::AdminOrganizationImporter creates it. The old logic gave the bot the plain name when the organization was the first organization. Cell 9 tried to claim the same username and email that the legacy cell already holds in the Topology Service. The commit failed with claim conflict: {bucket_type=CLAIM_TYPE_EMAIL bucket_value="gitlab-duo@staging.gitlab.com"} and the request returned a 500.
The same code path, Users::Internal#create_unique_internal, creates every internal bot: ghost, alert bot, support bot, security bot, automation bot, GitLab Duo, and admin bot. All of them had the same problem. Users::Internal.support_bot_id is called without an organization on issue pages and was affected too.
Fix
- A new private predicate
organization_suffix_required?inlib/users/internal.rbdecides when to add a suffix. WhenGitlab.config.cell.enabledis true, only the default organization keeps the plain username and email. Every other organization derives both from its organization path. Organization paths are already cluster-wide claims, so the values are unique by construction. - When cells are not enabled (GitLab Self-Managed, GitLab Dedicated, single-cell), behavior is unchanged. The first organization keeps the plain values. Other organizations get the organization path suffix as before.
- The email pattern now receives
-<organization path>before the numeric counter. Example:gitlab-duo-admin-org-cell-9-ab12cd34@staging.gitlab.com. - When no organization is passed, the suffix comes from the first organization on the cell. This makes
Users::Internal.support_bot_idsafe too. - The organization-scoped username in
organization_user_details.usernamestaysGitLabDuoin every case.@GitLabDuomentions and the documented behavior inside an organization do not change. - Display name logic is unchanged. It only needs to be distinct inside one cell.
- Existing bots are looked up by
user_typeand organization, never by literal username or email. No existing records need to change.
Resulting values
| Case | Username | Organization-scoped username | |
|---|---|---|---|
| Default organization on the legacy cell | GitLabDuo |
gitlab-duo@staging.gitlab.com |
GitLabDuo |
| Admin organization on Cell 9 | GitLabDuo_admin-org-cell-9-ab12cd34 |
gitlab-duo-admin-org-cell-9-ab12cd34@staging.gitlab.com |
GitLabDuo |
| Any other organization on any cell | GitLabDuo_<path> |
gitlab-duo-<path>@staging.gitlab.com |
GitLabDuo |
Alternatives considered
- Seed the bot in a production fixture like
db/fixtures/production/003_admin.rb. Rejected. Bots are per organization, fixtures run once at provisioning, Cell 9 is already provisioned, and the other bots would still collide. - Rescue
Cells::TransactionRecord::AlreadyClaimedErrorand retry with a random suffix. Rejected. It hides misconfiguration, costs a Topology Service round trip, and gives non-deterministic names.
Known limitation
Organization paths can be up to 255 characters. gitlab-duo-<path> can exceed the 64-character local-part limit from RFC 5321 when the path is longer than 53 characters. GitLab's email validation does not enforce that limit, the bot save runs with validate: false, and bots never receive mail, so this is cosmetic. The username GitLabDuo_<path> already had the same exposure before this change.
Tests
spec/lib/users/internal_spec.rb gained a when cells are enabled context in the shared bot users examples. It runs for all 7 bots and covers three cases: a non-default organization (suffixed username and email, plain organization-scoped username), the default organization (plain username and email), and no organization passed (suffix from the first organization). Result: 82 examples, 0 failures.
References
- Related to: gitlab-com/gl-infra/tenant-scale/cells-infrastructure/team#946 (closed)
- Bug report note: gitlab-com/gl-infra/tenant-scale/cells-infrastructure/team#941 (comment 3906607714)
- Work item: gitlab-com/gl-infra/tenant-scale/cells-infrastructure/team#941 (closed)
- Precedent for per-cell admin naming: !239295 (merged)
- Cells claims documentation: https://docs.gitlab.com/development/cells/claims/
Screenshots or screen recordings
No UI changes. This MR is backend only.
How to set up and validate locally
-
Run the spec:
bundle exec rspec spec/lib/users/internal_spec.rb -
To reproduce a non-legacy cell in GDK, add this to the
developmentsection ofconfig/gitlab.yml, then rungdk restart rails:cell: enabled: true id: 9 database: skip_sequence_alteration: trueskip_sequence_alteration: truekeeps Topology Service claims off, so no Topology Service is needed. -
In
gdk rails console:org = Organizations::Organization.create!( name: 'Cell 9 admin org', path: 'admin-org-cell-9-abcd1234', visibility_level: Organizations::Organization::PUBLIC, state: :active ) bot = Users::Internal.in_organization(org).duo_code_review_bot bot.username # => "GitLabDuo_admin-org-cell-9-abcd1234" bot.email # => "gitlab-duo-admin-org-cell-9-abcd1234@<your GDK host>" bot.organization_user_details.first.username # => "GitLabDuo" -
Bots for the default organization (id 1) keep the plain values. Remove the
cell:block afterwards and rungdk restart rails.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.