Add Container Virtual Registry routes and stub controller
Context
This is a series of MRs to implement Container image virtual registry: push/pull end... (#549131)
- Add the routes and stub controller
👈 WE ARE HERE - Modify HandleFileRequestService to handle both
by pathandby digestrequests - Implement pull (download) endpoints
- Implement push (upload) endpoints
- Cleanup: DRY up duplicated code between
VirtualRegistries::ContainerControllerandGroups::DependencyProxy::ApplicationController
The POC that integrates everything, and from which these MRs are being extracted from, is in !209585 (closed)
What does this MR do and why?
This MR introduces the routes and the VirtualRegistries::ContainerController class that handles the download endpoints that respond to docker pull <virtual registry url>. Only routing, authentication and authorization are covered by this MR. The actual implementation of the download will be done in follow-up MRs.
Changes included:
-
Routes (
config/routes/virtual_registry.rb):- Manifest endpoint:
GET /v2/virtual_registries/container/:id/*image/manifests/*tag_or_digest - Blob endpoint:
GET /v2/virtual_registries/container/:id/*image/blobs/:sha - Proper constraints for image names, tags, and SHA digests
- Route helpers for path generation
- Manifest endpoint:
-
Stub Controller (
ee/app/controllers/virtual_registries/container_controller.rb):- JWT authentication (copied from dependency proxy pattern)
- Feature flag validation (
container_virtual_registries) - License validation (
container_virtual_registry) - Permission validation (
read_virtual_registry) - Registry existence validation
- Returns
501 Not Implementedfor both endpoints
🗒️ Implementation Notes
For the most part, Container Virtual Registry code has been closely mirroring Maven Virtual Registry code. Here, we diverge, due to this constraint discussed in the issue:
Fixed url structures. Docker clients will not use a prefix and compute accessed urls from there. Instead, it will take the host, append
v2and then append whatever we have on the docker command. Thus, we can't really target the GitLab API endpoints (/api/v4) because docker clients, will use av2prefix:/v2/api/v4. This doesn't look great. Instead, we're going to use the same approach as the dependency proxy: use rails controllers mounted on the correct urls.
Like dependency proxy, we'll use a Rails controller instead of an API endpoint implemented with Grape. This is the dependency proxy flow:
JwtController -> DependencyProxyAuthenticationService -> DependencyProxyForContainersController#manifest
We'll reuse the first two parts, and implement a new controller, VirtualRegistries::ContainerController:
JwtController -> DependencyProxyAuthenticationService -> VirtualRegistries::ContainerController#manifest
How to validate locally
This MR puts the routes in place, but does not yet implement the actual image download behavior. For now, we can verify that docker login works, and docker pull gets an HTTP 501 Not Implemented response.
Setup
- Prepare a user and a
::VirtualRegistries::Container::Registry - Prepare a personal access token for the user, with
read_virtual_registrypermission - Enable the feature flag
container_virtual_registries - Enable dependency proxy
current_user = User.first # root or any user with read_virtual_registry permissions
registry = ::VirtualRegistries::Container::Registry.first
docker login
docker login gdk.test:3000/virtual_registries/container/<registry_id>
Username: <type current_user.username>
Password: <paste a personal access token with read_virtual_registry permission>
Login should succeed.
docker pull
docker pull gdk.test:3000/virtual_registries/container/5/hello-world:latest
Error response from daemon: failed to resolve reference "gdk.test:3000/virtual_registries/container/5/hello-world:latest": unexpected status from HEAD request to http://gdk.test:3000/v2/virtual_registries/container/5/hello-world/manifests/latest: 501 Not Implemented
We get the 501 Not Implemented response we have in our new controller.