Add internal org_mover API for organization maintenance
What does this MR do and why?
Adds an internal API for the org-mover
control plane to drive an organization's maintenance lifecycle via the
Organizations::Stateful state machine, plus state and readiness checks.
New endpoints under /api/v4/internal/org_mover/, authenticated by the
gitlab-shell JWT and gated per organization by an ops feature flag (see below):
| Method | Action | Transition |
|---|---|---|
GET |
maintenance_state |
reports { state: } (current lifecycle state) |
GET |
maintenance_readiness |
reports { ready: } (drain status) |
POST |
start_maintenance |
active → maintenance_initialization |
POST |
confirm_maintenance |
maintenance_initialization → maintenance |
POST |
cancel_maintenance |
maintenance_initialization → active |
POST |
exit_maintenance |
maintenance → active |
confirm_maintenance is guarded by Gitlab::Organizations::MaintenanceReadiness,
which currently reports false until the drain checks land in the follow-up, so
it returns 409 (retryable) until the organization is drained. Transitions run
under a row lock and are idempotent (already-in-target returns 204).
Every endpoint is gated by the org_mover_maintenance_api ops feature flag,
checked with the target organization as the actor, and returns 404 when the
flag is off. The flag is default-off and enabled per organization only for the
duration of a move, so the shared gitlab-shell token cannot drive maintenance on
an arbitrary organization. A dedicated credential is tracked in the follow-up
below.
References
- Part of gitlab-org#20404
- Readiness drain checks follow-up: #602822
- Dedicated secret and audience follow-up: #627692
How to set up and validate locally
Run the request specs:
bundle exec rspec spec/requests/api/internal/org_mover_spec.rbOr exercise the endpoints against a GDK. In the Rails console, enable the ops flag for a test organization, set it active, and mint a gitlab-shell token:
org = Organizations::Organization.create!(name: "T", path: "t-#{SecureRandom.hex(3)}",
visibility_level: Organizations::Organization::PUBLIC)
org.update!(state: :active)
Feature.enable(:org_mover_maintenance_api, org)
puts JSONWebToken::HMACToken.new(Gitlab::Shell.secret_token)
.tap { |t| t.issuer = Gitlab::Shell::JWT_ISSUER }.encodedThen call the endpoints with the org id and token:
curl -s -X POST https://gdk.test:3000/api/v4/internal/org_mover/start_maintenance \
-H "Gitlab-Shell-Api-Request: $TOK" -H "Content-Type: application/json" \
-d "{\"organization_id\": $ORG, \"maintenance_reason\": \"migration\"}" -w "\n%{http_code}\n"Expected: start_maintenance → 204; confirm_maintenance → 409 (not ready yet);
cancel_maintenance/exit_maintenance → 204; invalid reason → 400; unknown org
or flag disabled → 404; bad JWT → 401.
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.
Related to #602822