Add revoke endpoint for runner controller token API
What does this MR do and why?
Add revoke endpoint for runner controller token API.
This change adds the ability to revoke (disable) runner controller tokens. Tokens can be marked as "revoked" to prevent their use while keeping them in the system for audit purposes.
The implementation adds a status field to tokens that can be either "active" or "revoked", with active being the default. When listing or viewing tokens, only active ones are shown. A new API endpoint allows admins to revoke tokens.
The database is updated to store the token status, and comprehensive tests ensure the feature works correctly.
References
Part of #582812.
How to set up and validate locally
- Set up GDK and prepare instance admin's personal access token.
- Verify the endpoints using
curl.
export PAT="your-personal-access-token"
Prerequisites - create runner controller & token
Create runner controller if not exists:
curl --request POST \
--header "Content-Type: application/json" \
--header "PRIVATE-TOKEN: $PAT" \
"https://gdk.test:3443/api/v4/runner_controllers"
Create new token:
curl --request POST \
--header "Content-Type: application/json" \
--header "PRIVATE-TOKEN: $PAT" \
--data '{
"description": "Validates runner security settings before registration"
}' \
"https://gdk.test:3443/api/v4/runner_controllers/1/tokens"
Get tokens
curl --request GET \
--header "Content-Type: application/json" \
--header "PRIVATE-TOKEN: $PAT" \
"https://gdk.test:3443/api/v4/runner_controllers/1/tokens"
Revoke a token:
curl --request DELETE \
--header "Content-Type: application/json" \
--header "PRIVATE-TOKEN: $PAT" \
"https://gdk.test:3443/api/v4/runner_controllers/1/tokens/1"
Database query plans
List runner controller tokens
This MR adds a filter by status.
https://console.postgres.ai/gitlab/gitlab-production-ci/sessions/46385/commands/141491
SELECT "ci_runner_controller_tokens".*
FROM "ci_runner_controller_tokens"
WHERE "ci_runner_controller_tokens"."runner_controller_id" = 1
AND "ci_runner_controller_tokens"."status" = 0
ORDER BY "ci_runner_controller_tokens"."id" ASC limit 20 offset 0
Limit (cost=0.01..0.02 rows=1 width=98) (actual time=0.024..0.025 rows=0 loops=1)
Buffers: shared hit=3
-> Sort (cost=0.01..0.02 rows=1 width=98) (actual time=0.023..0.023 rows=0 loops=1)
Sort Key: ci_runner_controller_tokens.id
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=3
-> Seq Scan on public.ci_runner_controller_tokens (cost=0.00..0.00 rows=1 width=98) (actual time=0.003..0.004 rows=0 loops=1)
Filter: ((ci_runner_controller_tokens.runner_controller_id = 1) AND (ci_runner_controller_tokens.status = 0))
Rows Removed by Filter: 0
Settings: effective_cache_size = '338688MB', random_page_cost = '1.5', jit = 'off', seq_page_cost = '4', work_mem = '100MB'
Get a single token
This MR adds a filter by status.
https://console.postgres.ai/gitlab/gitlab-production-ci/sessions/46385/commands/141492
SELECT "ci_runner_controller_tokens".*
FROM "ci_runner_controller_tokens"
WHERE "ci_runner_controller_tokens"."runner_controller_id" = 1
AND "ci_runner_controller_tokens"."status" = 0
AND "ci_runner_controller_tokens"."id" = 13 limit 1
Limit (cost=0.00..0.00 rows=1 width=98) (actual time=0.005..0.006 rows=0 loops=1)
-> Seq Scan on public.ci_runner_controller_tokens (cost=0.00..0.00 rows=1 width=98) (actual time=0.004..0.005 rows=0 loops=1)
Filter: ((ci_runner_controller_tokens.runner_controller_id = 1) AND (ci_runner_controller_tokens.status = 0) AND (ci_runner_controller_tokens.id = 13))
Rows Removed by Filter: 0
Settings: effective_cache_size = '338688MB', random_page_cost = '1.5', jit = 'off', seq_page_cost = '4', work_mem = '100MB'
Revoke a token
This is a new query introduced by this MR when revoking it.
https://console.postgres.ai/gitlab/gitlab-production-ci/sessions/46385/commands/141493
UPDATE "ci_runner_controller_tokens"
SET "updated_at" = '2025-12-11 05:48:55.805493',
"status" = 1
WHERE "ci_runner_controller_tokens"."id" = 3
ModifyTable on public.ci_runner_controller_tokens (cost=0.00..0.00 rows=0 width=0) (actual time=0.003..0.003 rows=0 loops=1)
-> Seq Scan on public.ci_runner_controller_tokens (cost=0.00..0.00 rows=1 width=16) (actual time=0.002..0.002 rows=0 loops=1)
Filter: (ci_runner_controller_tokens.id = 3)
Rows Removed by Filter: 0
Settings: effective_cache_size = '338688MB', random_page_cost = '1.5', jit = 'off', seq_page_cost = '4', work_mem = '100MB'
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.