Add Organization read-only states to Organizations::Stateful
What does this MR do and why?
Adds two read-only states to the Organizations::Stateful state machine as step 1 of #602809 (closed) (part of epic gitlab-org#20404).
Design reference: ADR 010 - Organization Read-Only Mode
New states
| State | Integer | Description |
|---|---|---|
read_only_initialization |
5 | Writes blocked; organization is draining outstanding work |
read_only |
6 | Steady state; organization is fully drained and read-only |
New transition events
| Event | From | To | Description |
|---|---|---|---|
start_read_only |
active |
read_only_initialization |
Begin read-only transition |
confirm_read_only |
read_only_initialization |
read_only |
Complete the transition (drain finished) |
cancel_read_only |
read_only_initialization |
active |
Abort before freeze completes |
exit_read_only |
read_only |
active |
Return organization to service |
Guards
- Entering
read_only_initializationorread_onlyis rejected from any deletion state (soft_deleted,deletion_in_progress) and from pre-confirmation states (unconfirmed,confirmed). - The default organization (
id = DEFAULT_ORGANIZATION_ID) is explicitly blocked from entering read-only mode.
Reason tracking
start_read_only requires a read_only_reason keyword argument. Valid values: migration, isolation, incident, billing, legal. The reason is persisted in OrganizationDetail#state_metadata (existing JSONB column introduced in !230909 (merged)). The JSON schema is updated to allow the new field.
Predicate
read_only? returns true for both read_only_initialization and read_only. Enforcement layers in follow-up MRs must call this single predicate so that feature-flag gating (tracked in #602810) can be added cleanly later.
Scope
Model layer only. Controller/API/GraphQL/GitAccess enforcement comes in a separate follow-up MR.
References
- Issue: #602809 (closed)
- Epic: gitlab-org#20404
- Feature flag issue: #602810
- ADR 010: https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/organization/decisions/010_organization_read_only_mode/
- Unified state machine MR: !230909 (merged)
Screenshots or screen recordings
N/A — model-layer change only.
How to set up and validate locally
# In rails console:
org = Organizations::Organization.find(2) # any non-default org in active state
# Enter read-only initialization
org.start_read_only(read_only_reason: 'migration')
org.state_name # => :read_only_initialization
org.read_only? # => true
# Complete the transition
org.confirm_read_only
org.state_name # => :read_only
org.read_only? # => true
# Exit read-only
org.exit_read_only
org.state_name # => :active
org.read_only? # => false
# Default org is blocked
Organizations::Organization.default_organization.start_read_only(read_only_reason: 'migration')
# => false (with error: "start_read_only transition is not allowed for the default organization")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.