Add API for container virtual registry CRUD (registries)
What does this MR do and why?
In this MR, we add the following endpoints to manage the virtual registries.
🍋 API endpoints for VirtualRegistries::Container::Registry
Route | Notes |
---|---|
GET /groups/:id/-/virtual_registries/container/registries | List virtual registries for the given top level group |
POST /groups/:id/-/virtual_registries/container/registries | Create a new virtual registry |
GET /virtual_registries/container/registries/:id | Get the details of the virtual registry |
PATCH /virtual_registries/container/registries/:id | Update a virtual registry |
DELETE /virtual_registries/container/registries/:id | Delete a virtual registry |
We add the above endpoints behind the feature flag container_virtual_registry
and we also add a license check for this feature.
How to set up and validate locally
🍓 Prerequisites:
- Enable the feature flag
Feature.enable(:container_virtual_registry)
-
Have a **personal access token **ready. Here is a guide: https://docs.gitlab.com/user/profile/personal_access_tokens/#create-a-personal-access-token
-
Have a group ID ready
Group.first.id
-
Setup your GDK with an enterprise license (Premium). Follow these steps: https://gitlab.com/gitlab-org/customers-gitlab-com/-/blob/main/doc/setup/gitlab.md#adding-a-license-from-staging-customers-portal-to-your-gdk.
-
cURL
in your terminal or you may also use Postman (or similar)
🍓 Testing the endpoints:
1. Listing all virtual registries for the group: GET /groups/:id/-/virtual_registries/container/registries
curl --location 'http://gdk.test:3000/api/v4/groups/22/-/virtual_registries/container/registries' \
--header 'PRIVATE-TOKEN: glpat-xxxxx'
Result will look like, depending on what is in your database:
[
{
"id": 1,
"name": "Test Registry Record",
"description": "virtual registry #1",
"group_id": 22,
"created_at": "2025-08-22T08:30:43.147Z",
"updated_at": "2025-09-05T13:34:07.636Z"
},
{
"id": 3,
"name": "Test Registry Record 2",
"description": "virtual registry #2",
"group_id": 22,
"created_at": "2025-09-05T13:34:33.167Z",
"updated_at": "2025-09-05T13:34:33.167Z"
}
]
2. Creating a new virtual registry: POST /groups/:id/-/virtual_registries/container/registries
Feel free to update the name
and description
parameters.
curl --location --request POST 'http://gdk.test:3000/api/v4/groups/22/-/virtual_registries/container/registries?name=testCreate&description=viaAPI' \
--header 'PRIVATE-TOKEN: glpat-xxxxx'
Result will look something like:
{
"id": 5,
"name": "testCreate",
"description": "viaAPI",
"group_id": 22,
"created_at": "2025-09-05T13:39:28.182Z",
"updated_at": "2025-09-05T13:39:28.182Z"
}
You can try the GET request again from (1) and you should see this newly created virtual registry as a part of the list.
GET /virtual_registries/container/registries/:id
3. Get the details of a virtual registry: curl --location 'http://gdk.test:3000/api/v4/virtual_registries/container/registries/1' \
--header 'PRIVATE-TOKEN: glpat-xxxxx'
The result would look like:
{
"id": 1,
"name": "Test Registry Record",
"description": "virtual registry #1",
"group_id": 22,
"created_at": "2025-08-22T08:30:43.147Z",
"updated_at": "2025-09-05T13:34:07.636Z",
"registry_upstreams": [
{
"id": 2,
"position": 1,
"upstream_id": 2
},
{
"id": 6,
"position": 2,
"upstream_id": 5
},
{
"id": 5,
"position": 3,
"upstream_id": 3
}
]
}
It would include upstream information, if it exists.
4. Updating a virtual registry: PATCH /virtual_registries/container/registries/:id
curl --location --request PATCH 'http://gdk.test:3000/api/v4/virtual_registries/container/registries/1' \
--header 'PRIVATE-TOKEN: glpat-xxxxx' \
--form 'description="updated_description"'
Result would be a 200
if successful.
You can try doing the GET request in (3) and should see the updated values.
5. Deleting a virtual registry: DELETE /virtual_registries/container/registries/:id
curl --location --request DELETE 'http://gdk.test:3000/api/v4/virtual_registries/container/registries/1' \
--header 'PRIVATE-TOKEN: glpat-xxxxx'
Result would be a 204 No Content
if successful.
You can try again the the request in (1) to list all the virtual registries of the group and the deleted virtual registry would no longer be a part of it.
🍎 Database Query Plans
- Inserting a registry record
SQL Query
INSERT INTO "virtual_registries_container_registries" ("group_id", "created_at", "updated_at", "name", "description")
VALUES (22, '2025-09-06 11:56:02.224161', '2025-09-06 11:56:02.224161', 'SampleOnly', 'Testing')
RETURNING
"id"
Execution Plan
ModifyTable on public.virtual_registries_container_registries (cost=0.00..0.01 rows=1 width=96) (actual time=14.133..14.136 rows=1 loops=1)
Buffers: shared hit=76 read=7 dirtied=6 written=3
WAL: records=10 fpi=0 bytes=1161
I/O Timings: read=13.135 write=0.093
-> Result (cost=0.00..0.01 rows=1 width=96) (actual time=13.331..13.332 rows=1 loops=1)
Buffers: shared hit=11 read=5 dirtied=1
WAL: records=2 fpi=0 bytes=262
I/O Timings: read=13.048 write=0.000
Trigger RI_ConstraintTrigger_c_1067341093 for constraint fk_rails_4e95663af5: time=70.054 calls=1
Settings: work_mem = '100MB', effective_cache_size = '472585MB', jit = 'off', random_page_cost = '1.5', seq_page_cost = '4'
Details and visualization: https://postgres.ai/console/gitlab/gitlab-production-main/sessions/43164/commands/131976
- Updating a registry record
SQL Query
UPDATE
"virtual_registries_container_registries"
SET
"updated_at" = '2025-09-06 11:56:34.981811',
"description" = 'TestingTestingTesting'
WHERE
"virtual_registries_container_registries"."id" = 7
Execution Plan
ModifyTable on public.virtual_registries_container_registries (cost=0.14..3.16 rows=0 width=0) (actual time=0.026..0.027 rows=0 loops=1)
Buffers: shared hit=4
I/O Timings: read=0.000 write=0.000
-> Index Scan using virtual_registries_container_registries_pkey on public.virtual_registries_container_registries (cost=0.14..3.16 rows=1 width=46) (actual time=0.025..0.026 rows=0 loops=1)
Index Cond: (virtual_registries_container_registries.id = 7)
Buffers: shared hit=4
I/O Timings: read=0.000 write=0.000
Settings: work_mem = '100MB', effective_cache_size = '472585MB', jit = 'off', random_page_cost = '1.5', seq_page_cost = '4'
Details and visualization: https://postgres.ai/console/gitlab/gitlab-production-main/sessions/43164/commands/131977
- Deleting a registry record
SQL Query
DELETE FROM "virtual_registries_container_registries"
WHERE "virtual_registries_container_registries"."id" = 7
Execution Plan
ModifyTable on public.virtual_registries_container_registries (cost=0.14..3.16 rows=0 width=0) (actual time=0.029..0.029 rows=0 loops=1)
Buffers: shared hit=4
I/O Timings: read=0.000 write=0.000
-> Index Scan using virtual_registries_container_registries_pkey on public.virtual_registries_container_registries (cost=0.14..3.16 rows=1 width=6) (actual time=0.028..0.028 rows=0 loops=1)
Index Cond: (virtual_registries_container_registries.id = 7)
Buffers: shared hit=4
I/O Timings: read=0.000 write=0.000
Settings: effective_cache_size = '472585MB', jit = 'off', random_page_cost = '1.5', seq_page_cost = '4', work_mem = '100MB'
Details and visualization: https://postgres.ai/console/gitlab/gitlab-production-main/sessions/43164/commands/131979
- Fetching registries of the group
SQL Query
SELECT
"virtual_registries_container_registries".*
FROM
"virtual_registries_container_registries"
WHERE
"virtual_registries_container_registries"."group_id" = 22
Execution Plan
Seq Scan on public.virtual_registries_container_registries (cost=0.00..0.00 rows=1 width=96) (actual time=0.003..0.004 rows=0 loops=1)
Filter: (virtual_registries_container_registries.group_id = 22)
Rows Removed by Filter: 0
I/O Timings: read=0.000 write=0.000
Settings: random_page_cost = '1.5', seq_page_cost = '4', work_mem = '100MB', effective_cache_size = '472585MB', jit = 'off'
Details and visualization:: https://postgres.ai/console/gitlab/gitlab-production-main/sessions/43242/commands/132148
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 #548794