Skip to content

Add Artifact Registry services

David Fernandez requested to merge 439565-google-cloud-support-services into master

Context

In Google Artifact Registry Integration for Contai... (&12365 - closed), we're building the support for the Google Cloud Artifact Registry within GitLab.

In Refactor the GCP artifact registry Client (!142289 - merged), we refactored the client class that actually uses the Google Cloud API to read data about Artifact Registry repositories.

Time to implement the next step: services. This is issue GAR integration: services layer (#439565 - closed).

Basically, they will build and use the client class but also provide additional validations. Among others, check a feature flag.

We have 3 functions that the client exposes, so naturally, we split that into 3 services:

  • get the repository details. (a repository is basically the bucket containing the artifacts).
  • get the list of docker images. (listing the artifacts of a given bucket).
  • get the docker image details. (details of a given artifact).

All 3 services will have a similar validation and error handling, thus we have a base class that will centralize the common logic.

The main user of these services will be GraphQL endpoints (implemented in different MRs). See GraphQL: get GAR artifacts from project (#425149 - closed) for example.

🔬 What does this MR do and why?

  • Add (or update) 3 services:
    • GoogleCloudPlatform::ArtifactRegistry::GetDockerImageService.
    • GoogleCloudPlatform::ArtifactRegistry::ListDockerImagesService.
    • GoogleCloudPlatform::ArtifactRegistry::GetRepositoryService.
  • Add a base (common parent) service class: GoogleCloudPlatform::ArtifactRegistry::BaseService.
  • Add or update the related specs.

These changes are behind an existing feature flag : https://gitlab.com/gitlab-org/gitlab/-/issues/435673.

🏎 MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

🦄 Screenshots or screen recordings

These are services so, hu... no impact on the existing UI 😸

How to set up and validate locally

🛠 Setup

Local testing is a bit involved as we need to have an Artifact Registry set up and some other things for the authentication part. Follow !142289 (merged), points 1️⃣ and 2️⃣ and come back here then.

Now, we need to set up the Artifact registry integration. Follow the instructions from !141127 (merged).

Enable the gcp_artifact_registry feature flag:

$ echo "Feature.enable(:gcp_artifact_registry)" | gdk rails console

All the examples below are to be executed in the rails console and you will need the project_id on which you set up the integration and a user with read_container_image permission (reporter+).

1️⃣ Get repository details

GoogleCloudPlatform::ArtifactRegistry::GetRepositoryService.new(project: Project.find(<project_id>), current_user: User.first).execute
=> #<ServiceResponse:0x000000016919b558
 @http_status=:ok,
 @message=nil,
 @payload=
  <Google::Cloud::ArtifactRegistry::V1::Repository: ...>,
 @reason=nil,
 @status=:success>

2️⃣ List docker images

GoogleCloudPlatform::ArtifactRegistry::ListDockerImagesService.new(project: Project.find(<project_id>), current_user: User.first).execute(order_by: 'name desc', page_size: 20)
=> => #<ServiceResponse:0x00000001697f5d40
 @http_status=:ok,
 @message=nil,
 @payload=
  <Google::Cloud::ArtifactRegistry::V1::ListDockerImagesResponse: docker_images: [<Google::Cloud::ArtifactRegistry::V1::DockerImage: ...], next_page_token: "AHbNynEc7zp3pIfvtDDiFkp...">,
 @reason=nil,
 @status=:success>

3️⃣ Get docker image

GoogleCloudPlatform::ArtifactRegistry::GetDockerImageService.new(project: Project.find(<project_id>), current_user: User.first).execute(name: '<image_name>')
=> #<ServiceResponse:0x000000015ec73ee0
 @http_status=:ok,
 @message=nil,
 @payload=
  <Google::Cloud::ArtifactRegistry::V1::DockerImage: ...>,
 @reason=nil,
 @status=:success>

4️⃣ Handling errors

Let's play around with erroneous calls:

GoogleCloudPlatform::ArtifactRegistry::ListDockerImagesService.new(project: Project.find(267), current_user: User.first).execute
=> #<ServiceResponse:0x000000013a40d8b0 @http_status=nil, @message="Invalid page_size value", @payload={}, @reason=nil, @status=:error>

GoogleCloudPlatform::ArtifactRegistry::ListDockerImagesService.new(project: Project.find(267), current_user: User.first).execute(order_by: 'foobar desc', page_size: 20)
=> #<ServiceResponse:0x000000015fcb2a90 @http_status=nil, @message="Invalid order_by value", @payload={}, @reason=nil, @status=:error>

GoogleCloudPlatform::ArtifactRegistry::GetDockerImageService.new(project: Project.find(267), current_user: User.first).execute(name: 'idontexist')
=> #<ServiceResponse:0x0000000164e7eba8 @http_status=nil, @message="Unsuccessful Google Cloud API request", @payload={}, @reason=nil, @status=:error>

Let's update the repository name for a repostory that doesn't host docker images:

GoogleCloudPlatform::ArtifactRegistry::ListDockerImagesService.new(project: Project.find(267), current_user: User.first).execute(page_size: 20)
=> #<ServiceResponse:0x00000001664d6180 @http_status=nil, @message="Unsuccessful Google Cloud API request", @payload={}, @reason=nil, @status=:error>

In addition to the above, we have error messages in the log/application_json.log file. Example:

{"severity":"ERROR","time":"2024-02-05T15:34:32.134Z","class_name":"GoogleCloudPlatform::ArtifactRegistry::GetRepositoryService","project_id":267,"message":"7:Permission 'artifactregistry.repositories.get' denied on resource '<image name>' (or it may not exist).. debug_error_string:..."}
Edited by David Fernandez

Merge request reports