Skip to content

Add the GCP technical demo service

David Fernandez requested to merge 433514-demo-service into master

🔭 Context

This MR adds the underlying objects required for the demo needs from #433514 :

  1. A client to access an API.
    • This requires a JWT with specific claims. A dedicated class is added for this.
  2. A service to call the related function.

In this MR, the service is not connected to any codebase logic.

This will be done in a follow up MR, see !139802 (merged).

Still, the service that runs at the project level will require the user to have the read_container_images permission.

The objects introduced in this MR are going to be re-used for future evolutions.

🔍 What does this MR do and why?

  • Add a Artifact Registry Client.
    • Add the related helper class to build the proper JWT.
  • Add a list docker images service.
  • Add the related specs.

This code is not connected to anything (yet). See the follow up !139802 (merged).

🖼 Screenshots or screen recordings

None

How to set up and validate locally

It is quite challenging to set up the client class so that it works properly. As such, to test this locally, we suggest using a dummy response.

Update lib/integrations/google_cloud_platform/artifact_registry/client.rb with:

diff
diff --git a/lib/integrations/google_cloud_platform/artifact_registry/client.rb b/lib/integrations/google_cloud_platform/artifact_registry/client.rb
index ae41aa2614ef..e1b12ec12a43 100644
--- a/lib/integrations/google_cloud_platform/artifact_registry/client.rb
+++ b/lib/integrations/google_cloud_platform/artifact_registry/client.rb
@@ -15,18 +15,19 @@ def initialize(project:, user:, gcp_project_id:, gcp_location:, gcp_repository:,
         end
 
         def list_docker_images(page_token: nil)
-          response = ::Gitlab::HTTP.get(
-            list_docker_images_url,
-            headers: headers,
-            query: query_params(page_token: page_token),
-            format: :plain # disable httparty json parsing
-          )
-
-          if response.success?
-            ::Gitlab::Json.parse(response.body, symbolize_keys: true)
-          else
-            {}
-          end
+          # response = ::Gitlab::HTTP.get(
+          #   list_docker_images_url,
+          #   headers: headers,
+          #   query: query_params(page_token: page_token),
+          #   format: :plain # disable httparty json parsing
+          # )
+
+          # if response.success?
+          #   ::Gitlab::Json.parse(response.body, symbolize_keys: true)
+          # else
+          #   {}
+          # end
+          ::Gitlab::Json.parse(dummy_response, symbolize_keys: true)
         end
 
         private
@@ -51,6 +52,79 @@ def headers
             'Authorization' => "Bearer #{jwt}"
           }
         end
+
+        def dummy_response
+           <<~BODY
+          {
+    "images": [
+        {
+            "built_at": "2023-11-30T23:23:11.980068941Z",
+            "media_type": "application/vnd.docker.distribution.manifest.v2+json",
+            "name": "projects/project/locations/location/repositories/my_dummy_repo/dockerImages/alpine@sha256:6a0657acfef760bd9e293361c9b558e98e7d740ed0dffca823d17098a4ffddf5",
+            "size_bytes": 2827903,
+            "tags": [
+                "3.15",
+                "3.15.11"
+            ],
+            "updated_at": "2023-12-07T11:48:50.840751Z",
+            "uploaded_at": "2023-12-07T11:48:47.598511Z",
+            "uri": "location-docker.pkg.dev/project/my_dummy_repo/alpine@sha256:6a0657acfef760bd9e293361c9b558e98e7d740ed0dffca823d17098a4ffddf5"
+        },
+        {
+            "built_at": "2023-11-30T23:22:59.087794357Z",
+            "media_type": "application/vnd.docker.distribution.manifest.v2+json",
+            "name": "projects/project/locations/location/repositories/my_dummy_repo/dockerImages/alpine@sha256:b6e5fbdf7e79701eb7c3b82928089980986395a8ab16f96cdc6e6874f618b375",
+            "size_bytes": 3380795,
+            "tags": [
+                "3.17",
+                "3.17.6"
+            ],
+            "updated_at": "2023-12-07T11:48:34.153599Z",
+            "uploaded_at": "2023-12-07T11:48:30.834559Z",
+            "uri": "location-docker.pkg.dev/project/my_dummy_repo/alpine@sha256:b6e5fbdf7e79701eb7c3b82928089980986395a8ab16f96cdc6e6874f618b375"
+        },
+        {
+            "built_at": "2023-11-30T23:23:05.499726235Z",
+            "media_type": "application/vnd.docker.distribution.manifest.v2+json",
+            "name": "projects/project/locations/location/repositories/my_dummy_repo/dockerImages/alpine@sha256:ca452b8ab373e6de9c39d030870a52b8f0d3a9cf998c43183fd114660ae96330",
+            "size_bytes": 2809254,
+            "tags": [
+                "3.16.8"
+            ],
+            "updated_at": "2023-12-07T11:48:40.510831Z",
+            "uploaded_at": "2023-12-07T11:48:40.510831Z",
+            "uri": "location-docker.pkg.dev/project/my_dummy_repo/alpine@sha256:ca452b8ab373e6de9c39d030870a52b8f0d3a9cf998c43183fd114660ae96330"
+        },
+        {
+            "built_at": "2023-11-30T23:22:52.738129857Z",
+            "media_type": "application/vnd.docker.distribution.manifest.v2+json",
+            "name": "projects/project/locations/location/repositories/my_dummy_repo/dockerImages/alpine@sha256:d695c3de6fcd8cfe3a6222b0358425d40adfd129a8a47c3416faff1a8aece389",
+            "size_bytes": 3403894,
+            "tags": [
+                "3",
+                "3.18",
+                "3.18.5",
+                "latest"
+            ],
+            "updated_at": "2023-12-07T11:48:54.439263Z",
+            "uploaded_at": "2023-12-07T11:48:19.187359Z",
+            "uri": "location-docker.pkg.dev/project/my_dummy_repo/alpine@sha256:d695c3de6fcd8cfe3a6222b0358425d40adfd129a8a47c3416faff1a8aece389"
+        },
+        {
+            "built_at": "2023-12-01T07:41:21.068714329Z",
+            "media_type": "application/vnd.docker.distribution.manifest.v2+json",
+            "name": "projects/project/locations/location/repositories/my_dummy_repo/dockerImages/nginx@sha256:06a3161e61728a5e8169639c65f1e6a9683bb128e1fa1469a90278bb8911e4cc",
+            "size_bytes": 5022486,
+            "tags": null,
+            "updated_at": "2023-12-07T11:47:17.633359Z",
+            "uploaded_at": "2023-12-07T11:47:17.633359Z",
+            "uri": "location-docker.pkg.dev/project/my_dummy_repo/nginx@sha256:06a3161e61728a5e8169639c65f1e6a9683bb128e1fa1469a90278bb8911e4cc"
+        }
+    ],
+    "next_page_token": "AHbNynGLcJEbHhPbupbMWiMZLK1SshEnP7zbqx2YImO-pPj3A6IQJ9TShNer8GbpOyvcheUo-SWIGEk8A8ZxrZLoTuliKoBJ0NG8lnWQbn24uRM_W4UzfykU0cdPMomvIgnt0JgurC2LN_dQffoT4d8Sg2XOJmUDZq7sRcn3kLz9HemXa40UrbVsu7qeDCc_fZsELzkeeHYmfQBIwPSbwYsuPA9Xjn-7KUDcsTluWtjw9ixMd_4ZKBus9qqQsvcWovS_5o5AL_8="
+}
+          BODY
+        end
       end
     end
   end

Then in a rails console:

Integrations::GoogleCloudPlatform::ArtifactRegistry::ListDockerImagesService.new(project: Project.first, current_user: User.first, params: { gcp_project_id: "test", gcp_location: "location", gcp_repository: "my_dummy_repo", gcp_wlif: "https://wlif.test" }).execute

You can try with an anomyous user on a private project:

Integrations::GoogleCloudPlatform::ArtifactRegistry::ListDockerImagesService.new(project: Project.private_only.first, current_user: nil, params: { gcp_project_id: "test", gcp_location: "location", gcp_repository: "my_dummy_repo", gcp_wlif: "https://wlif.test" }).execute

To test the JWT generation:

Integrations::GoogleCloudPlatform::Jwt.new(project: Project.first, user: User.first, claims: { audience: Integrations::GoogleCloudPlatform::BaseClient::GLGO_BASE_URL, wlif: "https://wlif.test/url"}).encoded

and then use https://jwt.io/ to decode the token.

🏎 MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by David Fernandez

Merge request reports

Loading