Add the GCP technical demo service
🔭 Context
This MR adds the underlying objects required for the demo needs from #433514 :
- A client to access an API.
- This requires a JWT with specific claims. A dedicated class is added for this.
- 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.
-
I have evaluated the MR acceptance checklist for this MR.