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_initialization or read_only is 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

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.

Merge request reports

Loading