diff --git a/ee/app/models/integrations/google_cloud_platform/artifact_registry.rb b/ee/app/models/integrations/google_cloud_platform/artifact_registry.rb index 8da8690bff3b75b2ab9827f6a9d33b7bde7361f5..3ce85b174cfb7dd582d3d1630b0f293c40fc15b8 100644 --- a/ee/app/models/integrations/google_cloud_platform/artifact_registry.rb +++ b/ee/app/models/integrations/google_cloud_platform/artifact_registry.rb @@ -91,7 +91,7 @@ def self.supported_events [] end - def wlif + def identity_provider_resource_name "//iam.googleapis.com/projects/#{workload_identity_pool_project_number}/" \ "locations/global/workloadIdentityPools/#{workload_identity_pool_id}/" \ "providers/#{workload_identity_pool_provider_id}" diff --git a/ee/app/services/google_cloud_platform/artifact_registry/base_project_service.rb b/ee/app/services/google_cloud_platform/artifact_registry/base_project_service.rb index ba8c41d9ca4cef035ea3b732de12007c393c0f2f..8c8a253615fde659a900b451b1b95fff3a868e04 100644 --- a/ee/app/services/google_cloud_platform/artifact_registry/base_project_service.rb +++ b/ee/app/services/google_cloud_platform/artifact_registry/base_project_service.rb @@ -24,6 +24,8 @@ def execute private + delegate :artifact_registry_location, :artifact_registry_repository, to: :project_integration, private: true + def validate_before_execute return ERROR_RESPONSES[:saas_only] unless Gitlab::Saas.feature_available?(:google_cloud_support) return ERROR_RESPONSES[:feature_flag_disabled] unless Feature.enabled?(:gcp_artifact_registry, project) @@ -39,31 +41,13 @@ def allowed? def client ::GoogleCloudPlatform::ArtifactRegistry::Client.new( - project: project, + project_integration: project_integration, user: current_user, - gcp_project_id: gcp_project_id, - gcp_location: gcp_location, - gcp_repository: gcp_repository, - gcp_wlif: gcp_wlif + artifact_registry_location: artifact_registry_location, + artifact_registry_repository: artifact_registry_repository ) end - def gcp_project_id - project_integration.artifact_registry_project_id - end - - def gcp_location - project_integration.artifact_registry_location - end - - def gcp_repository - project_integration.artifact_registry_repository - end - - def gcp_wlif - project_integration.wlif - end - def project_integration project.google_cloud_platform_artifact_registry_integration end diff --git a/ee/app/services/google_cloud_platform/compute/base_service.rb b/ee/app/services/google_cloud_platform/compute/base_service.rb index 97d9221f785d80141a6404888d0ffa7ef65a6442..b446cbf918a9633a432bd33ee53bd3db22f4cb2a 100644 --- a/ee/app/services/google_cloud_platform/compute/base_service.rb +++ b/ee/app/services/google_cloud_platform/compute/base_service.rb @@ -70,21 +70,12 @@ def valid_order_by?(value) def client ::GoogleCloudPlatform::Compute::Client.new( - project: project, + project_integration: project_integration, user: current_user, - gcp_project_id: gcp_project_id, - gcp_wlif: gcp_wlif + params: params.slice(:google_cloud_project_id) ) end - def gcp_project_id - params[:google_cloud_project_id] || project_integration.artifact_registry_project_id - end - - def gcp_wlif - project_integration.wlif - end - def project_integration project.google_cloud_platform_artifact_registry_integration end diff --git a/ee/lib/gitlab/ci/google_cloud/generate_build_environment_variables_service.rb b/ee/lib/gitlab/ci/google_cloud/generate_build_environment_variables_service.rb index ef41e1f58ce6ded4f465936ea1030f4e9d655518..441edf3d60bc380ce9409e899096287467840d53 100644 --- a/ee/lib/gitlab/ci/google_cloud/generate_build_environment_variables_service.rb +++ b/ee/lib/gitlab/ci/google_cloud/generate_build_environment_variables_service.rb @@ -14,7 +14,10 @@ def initialize(build) def execute return [] unless @integration&.active - config_json = ::GoogleCloudPlatform.credentials(audience: @integration.wlif, encoded_jwt: encoded_jwt).to_json + config_json = ::GoogleCloudPlatform.credentials( + identity_provider_resource_name: @integration.identity_provider_resource_name, + encoded_jwt: encoded_jwt + ).to_json var_attributes = { value: config_json, public: false, masked: true, file: true } [ @@ -26,7 +29,11 @@ def execute private def encoded_jwt - JwtV2.for_build(@build, aud: ::GoogleCloudPlatform::GLGO_BASE_URL, wlif: @integration.wlif) + JwtV2.for_build( + @build, + aud: ::GoogleCloudPlatform::GLGO_BASE_URL, + target_audience: @integration.identity_provider_resource_name + ) end end end diff --git a/ee/lib/google_cloud_platform.rb b/ee/lib/google_cloud_platform.rb index a2e0312118c01a08e9262bb46947e1b0b4c3e408..18f6ddd3a4f019d9e39ecddb58ef34561245cf7c 100644 --- a/ee/lib/google_cloud_platform.rb +++ b/ee/lib/google_cloud_platform.rb @@ -20,10 +20,10 @@ module GoogleCloudPlatform ApiError = Class.new(StandardError) AuthenticationError = Class.new(StandardError) - def self.credentials(audience:, encoded_jwt:) + def self.credentials(identity_provider_resource_name:, encoded_jwt:) { type: CREDENTIALS_TYPE, - audience: audience, + audience: identity_provider_resource_name, token_url: STS_URL, subject_token_type: SUBJECT_TOKEN_TYPE, credential_source: { diff --git a/ee/lib/google_cloud_platform/artifact_registry/client.rb b/ee/lib/google_cloud_platform/artifact_registry/client.rb index 76b85fe6d426d47c93d817750107916e16afcd05..05bd242e42800a034bb3d60c430dca74cc656c8e 100644 --- a/ee/lib/google_cloud_platform/artifact_registry/client.rb +++ b/ee/lib/google_cloud_platform/artifact_registry/client.rb @@ -13,14 +13,11 @@ class Client < ::GoogleCloudPlatform::BaseClient # This will use glgo and a workload identity federation instance to exchange # a JWT from GitLab for an access token to be used with the Google Cloud API. # - # +project+ The Project instance. + # +project_integration+ The project integration that contains project id and the identity + # provider resource name. # +user+ The User instance. - # +gcp_project_id+ The Google project_id as a string. Example: 'my-project'. - # +gcp_location+ The Google location string. Example: 'us-east1'. - # +gcp_repository+ The Google Artifact Registry repository name as a string. Example: 'repo'. - # +gcp_wlif+ The Google workload identity federation string. Similar to a URL but without the - # protocol. Example: - # '//iam.googleapis.com/projects/555/locations/global/workloadIdentityPools/pool/providers/sandbox'. + # +artifact_registry_location+ The Artifact Registry location string. Example: 'us-east1'. + # +artifact_registry_repository+ The Artifact Registry repository name as a string. Example: 'repo'. # # All parameters are required. # @@ -28,13 +25,15 @@ class Client < ::GoogleCloudPlatform::BaseClient # # +ArgumentError+ if one or more of the parameters is blank. # +RuntimeError+ if this is used outside the Saas instance. - def initialize(project:, user:, gcp_project_id:, gcp_location:, gcp_repository:, gcp_wlif:) - super(project: project, user: user, gcp_project_id: gcp_project_id, gcp_wlif: gcp_wlif) + def initialize(project_integration:, user:, artifact_registry_location:, artifact_registry_repository:) + super(project_integration: project_integration, user: user) - raise ArgumentError, BLANK_PARAMETERS_ERROR_MESSAGE if gcp_location.blank? || gcp_repository.blank? + if artifact_registry_location.blank? || artifact_registry_repository.blank? + raise ArgumentError, BLANK_PARAMETERS_ERROR_MESSAGE + end - @gcp_location = gcp_location - @gcp_repository = gcp_repository + @artifact_registry_location = artifact_registry_location + @artifact_registry_repository = artifact_registry_repository end # Get the Artifact Registry repository object and return it. @@ -54,7 +53,7 @@ def repository request = ::Google::Cloud::ArtifactRegistry::V1::GetRepositoryRequest.new(name: repository_full_name) handling_errors do - gcp_client.get_repository(request) + client.get_repository(request) end end @@ -93,7 +92,7 @@ def docker_images(page_size: nil, page_token: nil, order_by: nil) order_by: order_by ) handling_errors do - gcp_client.list_docker_images(request).response + client.list_docker_images(request).response end end @@ -116,13 +115,13 @@ def docker_image(name:) request = ::Google::Cloud::ArtifactRegistry::V1::GetDockerImageRequest.new(name: name) handling_errors do - gcp_client.get_docker_image(request) + client.get_docker_image(request) end end private - def gcp_client + def client ::Google::Cloud::ArtifactRegistry::V1::ArtifactRegistry::Client.new do |config| json_key_io = StringIO.new(::Gitlab::Json.dump(credentials)) ext_credentials = Google::Auth::ExternalAccount::Credentials.make_creds( @@ -132,10 +131,12 @@ def gcp_client config.credentials = ::Google::Cloud::ArtifactRegistry::V1::ArtifactRegistry::Credentials.new(ext_credentials) end end - strong_memoize_attr :gcp_client + strong_memoize_attr :client def repository_full_name - "projects/#{gcp_project_id}/locations/#{@gcp_location}/repositories/#{@gcp_repository}" + "projects/#{google_cloud_project_id}/" \ + "locations/#{@artifact_registry_location}/" \ + "repositories/#{@artifact_registry_repository}" end strong_memoize_attr :repository_full_name end diff --git a/ee/lib/google_cloud_platform/base_client.rb b/ee/lib/google_cloud_platform/base_client.rb index dc4239d2d6c5748a406571d813e314ff333e9f04..7b7a5ee51aff566a81c03379a3ed3453b7a1652e 100644 --- a/ee/lib/google_cloud_platform/base_client.rb +++ b/ee/lib/google_cloud_platform/base_client.rb @@ -14,12 +14,9 @@ class BaseClient # This will use glgo and a workload identity federation instance to exchange # a JWT from GitLab for an access token to be used with the Google Cloud API. # - # +project+ The Project instance. + # +project_integration+ The project integration that contains project id and the identity + # provider resource name. # +user+ The User instance. - # +gcp_project_id+ The Google project_id as a string. Example: 'my-project'. - # +gcp_wlif+ The Google workload identity federation string. Similar to a URL but without the - # protocol. Example: - # '//iam.googleapis.com/projects/555/locations/global/workloadIdentityPools/pool/providers/sandbox'. # # All parameters are required. # @@ -27,26 +24,24 @@ class BaseClient # # +ArgumentError+ if one or more of the parameters is blank. # +RuntimeError+ if this is used outside the SaaS instance. - def initialize(project:, user:, gcp_project_id:, gcp_wlif:) - if project.blank? || user.blank? || gcp_project_id.blank? || gcp_wlif.blank? - raise ArgumentError, BLANK_PARAMETERS_ERROR_MESSAGE - end - + def initialize(project_integration:, user:, params: {}) + raise ArgumentError, BLANK_PARAMETERS_ERROR_MESSAGE if project_integration.blank? || user.blank? raise SAAS_ONLY_ERROR_MESSAGE unless Gitlab::Saas.feature_available?(:google_cloud_support) - @project = project + @project_integration = project_integration @user = user - @gcp_project_id = gcp_project_id - @gcp_wlif = gcp_wlif + @params = params end private - attr_reader :project, :user, :gcp_project_id, :gcp_wlif + attr_reader :project_integration, :user, :params + + delegate :identity_provider_resource_name, to: :project_integration, prefix: :google_cloud def credentials ::GoogleCloudPlatform.credentials( - audience: gcp_wlif, + identity_provider_resource_name: google_cloud_identity_provider_resource_name, encoded_jwt: encoded_jwt ) end @@ -57,12 +52,20 @@ def encoded_jwt user: user, claims: { audience: GLGO_BASE_URL, - wlif: gcp_wlif + target_audience: google_cloud_identity_provider_resource_name } ) jwt.encoded end + def google_cloud_project_id + @project_integration.artifact_registry_project_id + end + + def project + @project_integration.project + end + def handling_errors yield rescue RuntimeError => e diff --git a/ee/lib/google_cloud_platform/compute/client.rb b/ee/lib/google_cloud_platform/compute/client.rb index be9734840fcf40e978631108bb0caad2d33bc58c..3e98aa75d03e7cfb216e51567577f836a77d48cf 100644 --- a/ee/lib/google_cloud_platform/compute/client.rb +++ b/ee/lib/google_cloud_platform/compute/client.rb @@ -6,6 +6,7 @@ module GoogleCloudPlatform module Compute class Client < ::GoogleCloudPlatform::BaseClient include Gitlab::Utils::StrongMemoize + extend ::Gitlab::Utils::Override COMPUTE_API_ENDPOINT = 'https://compute.googleapis.com' @@ -37,7 +38,7 @@ class Client < ::GoogleCloudPlatform::BaseClient # Google Cloud API. def regions(filter: nil, max_results: 500, order_by: nil, page_token: nil) request = ::Google::Cloud::Compute::V1::ListRegionsRequest.new( - project: @gcp_project_id, + project: google_cloud_project_id, filter: filter, max_results: max_results, order_by: order_by, @@ -76,7 +77,7 @@ def regions(filter: nil, max_results: 500, order_by: nil, page_token: nil) # Google Cloud API. def zones(filter: nil, max_results: 500, order_by: nil, page_token: nil) request = ::Google::Cloud::Compute::V1::ListZonesRequest.new( - project: @gcp_project_id, + project: google_cloud_project_id, filter: filter, max_results: max_results, order_by: order_by, @@ -116,7 +117,7 @@ def zones(filter: nil, max_results: 500, order_by: nil, page_token: nil) # Google Cloud API. def machine_types(zone:, filter: nil, max_results: 500, order_by: nil, page_token: nil) request = ::Google::Cloud::Compute::V1::ListMachineTypesRequest.new( - project: @gcp_project_id, + project: google_cloud_project_id, zone: zone, filter: filter, max_results: max_results, @@ -145,6 +146,11 @@ def zones_client end strong_memoize_attr :zones_client + override :google_cloud_project_id + def google_cloud_project_id + params[:google_cloud_project_id] || super + end + def client_for(klass) klass.new do |config| config.endpoint = COMPUTE_API_ENDPOINT diff --git a/ee/lib/google_cloud_platform/jwt.rb b/ee/lib/google_cloud_platform/jwt.rb index e9f7bb80f3bf2ee239f2570f01f49636bec1fcdc..b12a9bba6ca32cc2124f0ef2a8d375ac52842ac1 100644 --- a/ee/lib/google_cloud_platform/jwt.rb +++ b/ee/lib/google_cloud_platform/jwt.rb @@ -4,14 +4,14 @@ module GoogleCloudPlatform class Jwt < ::JSONWebToken::RSAToken extend ::Gitlab::Utils::Override - JWT_OPTIONS_ERROR = 'This jwt needs jwt claims audience and wlif to be set.' + JWT_OPTIONS_ERROR = 'This jwt needs jwt claims audience and target_audience to be set.' NoSigningKeyError = Class.new(StandardError) def initialize(project:, user:, claims:) super - raise ArgumentError, JWT_OPTIONS_ERROR if claims[:audience].blank? || claims[:wlif].blank? + raise ArgumentError, JWT_OPTIONS_ERROR if claims[:audience].blank? || claims[:target_audience].blank? @claims = claims @project = project @@ -34,8 +34,6 @@ def subject override :key_data def key_data @key_data ||= begin - # TODO Feels strange to use the CI signing key but do - # we have a different signing key? key_data = Gitlab::CurrentSettings.ci_jwt_signing_key raise NoSigningKeyError unless key_data @@ -56,7 +54,7 @@ def custom_claims user_login: @user&.username, user_email: @user&.email, user_access_level: user_access_level, - wlif: @claims[:wlif] + target_audience: @claims[:target_audience] } end diff --git a/ee/spec/lib/gitlab/ci/google_cloud/generate_build_environment_variables_service_spec.rb b/ee/spec/lib/gitlab/ci/google_cloud/generate_build_environment_variables_service_spec.rb index b620ceddaf8b2c127a10844216378ab7190704d0..f57b7634ebe992cffb7f0d7f81e30d8cad1fd00c 100644 --- a/ee/spec/lib/gitlab/ci/google_cloud/generate_build_environment_variables_service_spec.rb +++ b/ee/spec/lib/gitlab/ci/google_cloud/generate_build_environment_variables_service_spec.rb @@ -26,7 +26,7 @@ expect(Gitlab::Json.parse(execute.first[:value])).to match( 'type' => 'external_account', - 'audience' => integration.wlif, + 'audience' => integration.identity_provider_resource_name, 'subject_token_type' => 'urn:ietf:params:oauth:token-type:jwt', 'token_url' => 'https://sts.googleapis.com/v1/token', 'credential_source' => { @@ -48,7 +48,7 @@ 'project_id' => project.id.to_s, 'user_id' => user.id.to_s, 'aud' => 'https://auth.gcp.gitlab.com', - 'wlif' => integration.wlif + 'target_audience' => integration.identity_provider_resource_name )) end diff --git a/ee/spec/lib/google_cloud_platform/artifact_registry/client_spec.rb b/ee/spec/lib/google_cloud_platform/artifact_registry/client_spec.rb index 5c8ffac292db622b7801ddaa08dc135538ea05fb..081975996ce1dd5e05c1a6b7f9807fd91bd3f954 100644 --- a/ee/spec/lib/google_cloud_platform/artifact_registry/client_spec.rb +++ b/ee/spec/lib/google_cloud_platform/artifact_registry/client_spec.rb @@ -6,26 +6,22 @@ let_it_be(:project) { create(:project) } let_it_be(:rsa_key) { OpenSSL::PKey::RSA.generate(3072) } let_it_be(:rsa_key_data) { rsa_key.to_s } - - let(:gcp_project_id) { 'gcp_project_id' } - let(:gcp_location) { 'gcp_location' } - let(:gcp_repository) { 'gcp_repository' } - let(:gcp_wlif) { '//wlif.test' } + let_it_be(:project_integration) { create(:google_cloud_platform_artifact_registry_integration, project: project) } let(:user) { project.owner } + let(:artifact_registry_location) { project_integration&.artifact_registry_location } + let(:artifact_registry_repository) { project_integration&.artifact_registry_repository } let(:client) do described_class.new( - project: project, + project_integration: project_integration, user: user, - gcp_project_id: gcp_project_id, - gcp_location: gcp_location, - gcp_repository: gcp_repository, - gcp_wlif: gcp_wlif + artifact_registry_location: artifact_registry_location, + artifact_registry_repository: artifact_registry_repository ) end - shared_context 'with a gcp client double' do - let(:gcp_client_double) { instance_double('::Google::Cloud::ArtifactRegistry::V1::ArtifactRegistry::Client') } + shared_context 'with a client double' do + let(:client_double) { instance_double('::Google::Cloud::ArtifactRegistry::V1::ArtifactRegistry::Client') } let(:config_double) do instance_double('Google::Cloud::ArtifactRegistry::V1::ArtifactRegistry::Client::Configuration') end @@ -41,15 +37,19 @@ .with(instance_of(::Google::Cloud::ArtifactRegistry::V1::ArtifactRegistry::Credentials)) allow(::Google::Cloud::ArtifactRegistry::V1::ArtifactRegistry::Client).to receive(:new) do |_, &block| block.call(config_double) - gcp_client_double + client_double end + + # required so that google auth gem will not trigger any API request + allow(project_integration).to receive(:identity_provider_resource_name) + .and_return('//identity.provider.resource.name.test') end end - shared_examples 'handling errors' do |gcp_client_method:| + shared_examples 'handling errors' do |client_method:| shared_examples 'transforming the error' do |message:, from_klass:, to_klass:| it "translates the error from #{from_klass} to #{to_klass}" do - expect(gcp_client_double).to receive(gcp_client_method).and_raise(from_klass, message) + expect(client_double).to receive(client_method).and_raise(from_klass, message) expect { subject }.to raise_error(to_klass, message) end @@ -87,8 +87,8 @@ end end - context 'with a nil project' do - let(:project) { nil } + context 'with a nil project integration' do + let(:project_integration) { nil } let(:user) { build(:user) } it_behaves_like 'raising an error with', @@ -104,7 +104,10 @@ ::GoogleCloudPlatform::BaseClient::BLANK_PARAMETERS_ERROR_MESSAGE end - %i[gcp_project_id gcp_location gcp_repository gcp_wlif].each do |field| + %i[ + artifact_registry_location + artifact_registry_repository + ].each do |field| context "with a nil #{field}" do let(field) { nil } @@ -122,23 +125,23 @@ end describe '#repository' do - include_context 'with a gcp client double' + include_context 'with a client double' subject(:repository) { client.repository } it 'returns the expected response' do - expect(gcp_client_double).to receive(:get_repository) + expect(client_double).to receive(:get_repository) .with(instance_of(::Google::Cloud::ArtifactRegistry::V1::GetRepositoryRequest)) .and_return(dummy_response) expect(repository).to eq(dummy_response) end - it_behaves_like 'handling errors', gcp_client_method: :get_repository + it_behaves_like 'handling errors', client_method: :get_repository end describe '#docker_images' do - include_context 'with a gcp client double' + include_context 'with a client double' let(:page_size) { nil } let(:page_token) { nil } @@ -154,7 +157,7 @@ shared_examples 'returning the expected response' do |expected_page_size: described_class::DEFAULT_PAGE_SIZE| it 'returns the expected response' do - expect(gcp_client_double).to receive(:list_docker_images) do |request| + expect(client_double).to receive(:list_docker_images) do |request| expect(request).to be_a ::Google::Cloud::ArtifactRegistry::V1::ListDockerImagesRequest expect(request.page_size).to eq(expected_page_size) expect(request.page_token).to eq(page_token.to_s) @@ -187,18 +190,18 @@ it_behaves_like 'returning the expected response' end - it_behaves_like 'handling errors', gcp_client_method: :list_docker_images + it_behaves_like 'handling errors', client_method: :list_docker_images end describe '#docker_image' do - include_context 'with a gcp client double' + include_context 'with a client double' let(:name) { 'test' } subject(:docker_image) { client.docker_image(name: name) } it 'returns the expected response' do - expect(gcp_client_double).to receive(:get_docker_image) do |request| + expect(client_double).to receive(:get_docker_image) do |request| expect(request).to be_a ::Google::Cloud::ArtifactRegistry::V1::GetDockerImageRequest expect(request.name).to eq(name) @@ -208,7 +211,7 @@ expect(docker_image).to eq(dummy_response) end - it_behaves_like 'handling errors', gcp_client_method: :get_docker_image + it_behaves_like 'handling errors', client_method: :get_docker_image end def stub_authentication_requests diff --git a/ee/spec/lib/google_cloud_platform/compute/client_spec.rb b/ee/spec/lib/google_cloud_platform/compute/client_spec.rb index 3bfddda200c988a7292aeca7abb0ac138a0676af..2fc2bba3bfb5f5cdccdd20c7caf7de41cb5e2d6a 100644 --- a/ee/spec/lib/google_cloud_platform/compute/client_spec.rb +++ b/ee/spec/lib/google_cloud_platform/compute/client_spec.rb @@ -6,22 +6,21 @@ let_it_be(:project) { create(:project) } let_it_be(:rsa_key) { OpenSSL::PKey::RSA.generate(3072) } let_it_be(:rsa_key_data) { rsa_key.to_s } + let_it_be(:project_integration) { create(:google_cloud_platform_artifact_registry_integration, project: project) } - let(:gcp_project_id) { 'cloud_project_id' } - let(:gcp_wlif) { '//wlif.test' } + let(:google_cloud_project_id) { 'project_id' } + let(:google_cloud_identity_provider_resource_name) { '//identity.provider.resource.name.test' } let(:user) { project.owner } let(:client) do described_class.new( - project: project, - user: user, - gcp_project_id: gcp_project_id, - gcp_wlif: gcp_wlif + project_integration: project_integration, + user: user ) end - shared_context 'with a gcp client double' do |client_klass:| - let(:gcp_client_double) { instance_double(client_klass.to_s) } + shared_context 'with a client double' do |client_klass:| + let(:client_double) { instance_double(client_klass.to_s) } let(:config_double) { instance_double("#{client_klass}::Configuration") } let(:dummy_response) { Object.new } @@ -35,15 +34,19 @@ .with(instance_of(::Google::Cloud::Compute::V1::Instances::Credentials)) allow(client_klass).to receive(:new) do |_, &block| block.call(config_double) - gcp_client_double + client_double end + + # required so that google auth gem will not trigger any API request + allow(project_integration).to receive(:identity_provider_resource_name) + .and_return('//identity.provider.resource.name.test') end end - shared_examples 'handling errors' do |gcp_client_method:| + shared_examples 'handling errors' do |client_method:| shared_examples 'transforming the error' do |message:, from_klass:, to_klass:| it "translates the error from #{from_klass} to #{to_klass}" do - expect(gcp_client_double).to receive(gcp_client_method).and_raise(from_klass, message) + expect(client_double).to receive(client_method).and_raise(from_klass, message) expect { subject }.to raise_error(to_klass, message) end @@ -81,8 +84,8 @@ end end - context 'with a nil project' do - let(:project) { nil } + context 'with a nil project integration' do + let(:project_integration) { nil } let(:user) { build(:user) } it_behaves_like 'raising an error with', @@ -98,14 +101,6 @@ ::GoogleCloudPlatform::BaseClient::BLANK_PARAMETERS_ERROR_MESSAGE end - %i[gcp_project_id gcp_wlif].each do |field| - context "with a nil #{field}" do - let(field) { nil } - - it_behaves_like 'raising an error with', ArgumentError, described_class::BLANK_PARAMETERS_ERROR_MESSAGE - end - end - context 'when not on saas' do before do stub_saas_features(google_cloud_support: false) @@ -116,7 +111,7 @@ end describe '#regions' do - include_context 'with a gcp client double', client_klass: Google::Cloud::Compute::V1::Regions::Rest::Client + include_context 'with a client double', client_klass: Google::Cloud::Compute::V1::Regions::Rest::Client let(:filter) { nil } let(:max_results) { 500 } @@ -132,7 +127,7 @@ shared_examples 'returning the expected response' do it 'returns the expected response' do - expect(gcp_client_double).to receive(:list) do |request| + expect(client_double).to receive(:list) do |request| expect(request).to be_a ::Google::Cloud::Compute::V1::ListRegionsRequest expect(request.filter).to eq(filter.to_s) expect(request.max_results).to eq(max_results) @@ -172,11 +167,11 @@ it_behaves_like 'returning the expected response' end - it_behaves_like 'handling errors', gcp_client_method: :list + it_behaves_like 'handling errors', client_method: :list end describe '#zones' do - include_context 'with a gcp client double', client_klass: Google::Cloud::Compute::V1::Zones::Rest::Client + include_context 'with a client double', client_klass: Google::Cloud::Compute::V1::Zones::Rest::Client let(:filter) { nil } let(:max_results) { 500 } @@ -192,7 +187,7 @@ shared_examples 'returning the expected response' do it 'returns the expected response' do - expect(gcp_client_double).to receive(:list) do |request| + expect(client_double).to receive(:list) do |request| expect(request).to be_a ::Google::Cloud::Compute::V1::ListZonesRequest expect(request.filter).to eq(filter.to_s) expect(request.max_results).to eq(max_results) @@ -232,11 +227,11 @@ it_behaves_like 'returning the expected response' end - it_behaves_like 'handling errors', gcp_client_method: :list + it_behaves_like 'handling errors', client_method: :list end describe '#machine_types' do - include_context 'with a gcp client double', client_klass: Google::Cloud::Compute::V1::MachineTypes::Rest::Client + include_context 'with a client double', client_klass: Google::Cloud::Compute::V1::MachineTypes::Rest::Client let(:zone) { 'europe-west4-a' } let(:filter) { nil } @@ -255,7 +250,7 @@ shared_examples 'returning the expected response' do it 'returns the expected response' do - expect(gcp_client_double).to receive(:list) do |request| + expect(client_double).to receive(:list) do |request| expect(request).to be_a ::Google::Cloud::Compute::V1::ListMachineTypesRequest expect(request.zone).to eq(zone.to_s) expect(request.filter).to eq(filter.to_s) @@ -296,7 +291,7 @@ it_behaves_like 'returning the expected response' end - it_behaves_like 'handling errors', gcp_client_method: :list + it_behaves_like 'handling errors', client_method: :list end def stub_authentication_requests diff --git a/ee/spec/lib/google_cloud_platform/jwt_spec.rb b/ee/spec/lib/google_cloud_platform/jwt_spec.rb index 490ba3021e7183fe593b02f28e174bd026e367de..763b32ee9d193def27817f93fdb0ade5144766cf 100644 --- a/ee/spec/lib/google_cloud_platform/jwt_spec.rb +++ b/ee/spec/lib/google_cloud_platform/jwt_spec.rb @@ -6,7 +6,7 @@ let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } - let(:claims) { { audience: 'http://sandbox.test', wlif: 'http://wlif.test' } } + let(:claims) { { audience: 'http://sandbox.test', target_audience: 'audience' } } let(:jwt) { described_class.new(project: project, user: user, claims: claims) } describe '#encoded' do @@ -25,7 +25,7 @@ expect(payload).to include( 'root_namespace_path' => project.root_namespace.full_path, 'root_namespace_id' => project.root_namespace.id.to_s, - 'wlif' => claims[:wlif], + 'target_audience' => claims[:target_audience], 'aud' => claims[:audience], 'project_id' => project.id.to_s, 'project_path' => project.full_path, @@ -54,15 +54,15 @@ end context 'with missing jwt audience' do - let(:claims) { { wlif: 'http://wlif.test' } } + let(:claims) { super().merge(audience: nil) } it 'raises an ArgumentError' do expect { encoded }.to raise_error(ArgumentError, described_class::JWT_OPTIONS_ERROR) end end - context 'with missing jwt wlif' do - let(:claims) { { audience: 'http://sandbox.test' } } + context 'with missing jwt target_audience' do + let(:claims) { super().merge(target_audience: nil) } it 'raises an ArgumentError' do expect { encoded }.to raise_error(ArgumentError, described_class::JWT_OPTIONS_ERROR) diff --git a/ee/spec/models/integrations/google_cloud_platform/artifact_registry_spec.rb b/ee/spec/models/integrations/google_cloud_platform/artifact_registry_spec.rb index 3872425c167404cba586f59c34c95ca0e9af471a..45ff1bb0f0ae297dda5727e3d973bc16df3b21b3 100644 --- a/ee/spec/models/integrations/google_cloud_platform/artifact_registry_spec.rb +++ b/ee/spec/models/integrations/google_cloud_platform/artifact_registry_spec.rb @@ -75,16 +75,16 @@ it { is_expected.to eq([]) } end - describe '#wlif' do - let(:wlif) do + describe '#identity_provider_resource_name' do + let(:expected) do "//iam.googleapis.com/projects/#{integration.workload_identity_pool_project_number}/" \ "locations/global/workloadIdentityPools/#{integration.workload_identity_pool_id}/" \ "providers/#{integration.workload_identity_pool_provider_id}" end - subject { integration.wlif } + subject { integration.identity_provider_resource_name } - it { is_expected.to eq(wlif) } + it { is_expected.to eq(expected) } end describe '#testable?' do diff --git a/ee/spec/requests/api/graphql/project/google_cloud/artifact_registry/docker_images_spec.rb b/ee/spec/requests/api/graphql/project/google_cloud/artifact_registry/docker_images_spec.rb index 3a0f5038887665bf686de28dba8448f68f47eaeb..a820e3523a4cdcc53d5f221d6cafc7d0ff6e8b68 100644 --- a/ee/spec/requests/api/graphql/project/google_cloud/artifact_registry/docker_images_spec.rb +++ b/ee/spec/requests/api/graphql/project/google_cloud/artifact_registry/docker_images_spec.rb @@ -8,14 +8,16 @@ include GoogleApi::CloudPlatformHelpers let_it_be(:project) { create(:project) } - let_it_be(:project_integration) { create(:google_cloud_platform_artifact_registry_integration, project: project) } - let_it_be(:user) { project.first_owner } + let_it_be_with_refind(:project_integration) do + create(:google_cloud_platform_artifact_registry_integration, project: project) + end let_it_be(:artifact_registry_repository_url) do "https://console.cloud.google.com/artifacts/docker/#{project_integration.artifact_registry_project_id}/" \ "#{project_integration.artifact_registry_location}/#{project_integration.artifact_registry_repository}" end + let(:user) { project.first_owner } let(:image) { 'ruby' } let(:digest) { 'sha256:4ca5c21b' } let(:client_double) { instance_double('::GoogleCloudPlatform::ArtifactRegistry::Client') } @@ -86,12 +88,10 @@ allow(::GoogleCloudPlatform::ArtifactRegistry::Client).to receive(:new) .with( - project: project, + project_integration: project_integration, user: user, - gcp_project_id: project_integration.artifact_registry_project_id, - gcp_location: project_integration.artifact_registry_location, - gcp_repository: project_integration.artifact_registry_repository, - gcp_wlif: project_integration.wlif + artifact_registry_location: project_integration.artifact_registry_location, + artifact_registry_repository: project_integration.artifact_registry_repository ).and_return(client_double) allow(client_double).to receive(:docker_images) diff --git a/ee/spec/requests/api/graphql/project/runner_google_cloud_provisioning_options_spec.rb b/ee/spec/requests/api/graphql/project/runner_google_cloud_provisioning_options_spec.rb index 8fffbe7ac976653a51e62717a3445de8de28515d..e557aef314be247db78973307defd2e68c8cfcaa 100644 --- a/ee/spec/requests/api/graphql/project/runner_google_cloud_provisioning_options_spec.rb +++ b/ee/spec/requests/api/graphql/project/runner_google_cloud_provisioning_options_spec.rb @@ -14,10 +14,9 @@ let(:google_cloud_project_id) { 'project_id_override' } let(:expected_compute_client_args) do { - project: project, + project_integration: integration, user: current_user, - gcp_project_id: google_cloud_project_id, - gcp_wlif: integration.wlif + params: { google_cloud_project_id: google_cloud_project_id } } end diff --git a/ee/spec/support/shared_contexts/google_cloud_platform/artifact_registry/services_shared_contexts.rb b/ee/spec/support/shared_contexts/google_cloud_platform/artifact_registry/services_shared_contexts.rb index 3a29f7ffc5cb8c3c3fe2fdaa09837abf79c45079..78ca0525c04d2e3726e2c480b2a4b59b2dd624cd 100644 --- a/ee/spec/support/shared_contexts/google_cloud_platform/artifact_registry/services_shared_contexts.rb +++ b/ee/spec/support/shared_contexts/google_cloud_platform/artifact_registry/services_shared_contexts.rb @@ -23,12 +23,10 @@ before do allow(::GoogleCloudPlatform::ArtifactRegistry::Client).to receive(:new) .with( - project: project, + project_integration: project_integration, user: user, - gcp_project_id: project_integration.artifact_registry_project_id, - gcp_location: project_integration.artifact_registry_location, - gcp_repository: project_integration.artifact_registry_repository, - gcp_wlif: project_integration.wlif + artifact_registry_location: project_integration.artifact_registry_location, + artifact_registry_repository: project_integration.artifact_registry_repository ).and_return(client_double) end end diff --git a/ee/spec/support/shared_contexts/google_cloud_platform/compute/services_shared_contexts.rb b/ee/spec/support/shared_contexts/google_cloud_platform/compute/services_shared_contexts.rb index f5879130772d1adf00906c14b80cf9a25692d45b..f3b4da5c4b81ffe9f40f8534b6e1565fac88fd55 100644 --- a/ee/spec/support/shared_contexts/google_cloud_platform/compute/services_shared_contexts.rb +++ b/ee/spec/support/shared_contexts/google_cloud_platform/compute/services_shared_contexts.rb @@ -21,10 +21,9 @@ before do allow(::GoogleCloudPlatform::Compute::Client).to receive(:new) .with( - project: project, + project_integration: project_integration, user: user, - gcp_project_id: google_cloud_project_id || project_integration.artifact_registry_project_id, - gcp_wlif: project_integration.wlif + params: params.slice(:google_cloud_project_id) ).and_return(client_double) end end diff --git a/ee/spec/support/shared_examples/google_cloud_platform/compute/services_shared_examples.rb b/ee/spec/support/shared_examples/google_cloud_platform/compute/services_shared_examples.rb index 28fe29b918ffca9fab402e3575995fce73398505..05f3872fa0f7c4a796db184e61b77efc59fce96d 100644 --- a/ee/spec/support/shared_examples/google_cloud_platform/compute/services_shared_examples.rb +++ b/ee/spec/support/shared_examples/google_cloud_platform/compute/services_shared_examples.rb @@ -74,12 +74,11 @@ it 'returns results by calling the specified project id' do expect(::GoogleCloudPlatform::Compute::Client).to receive(:new) - .with( - project: project, - user: user, - gcp_project_id: google_cloud_project_id, - gcp_wlif: project_integration.wlif - ).and_return(client_double) + .with(project_integration: project_integration, user: user, params: extra_params) do |**args| + expect(args.dig(:params, :google_cloud_project_id)).to eq(google_cloud_project_id) + + client_double + end expect(response).to be_success expect(response.payload[:items]).to be_a Enumerable diff --git a/lib/gitlab/ci/jwt_v2.rb b/lib/gitlab/ci/jwt_v2.rb index 95cc84d16b520f9a5f8b48814ed64ebafbe2c02f..0ae4cfb35f045df6a4d8103231fcc460f1ccf9f9 100644 --- a/lib/gitlab/ci/jwt_v2.rb +++ b/lib/gitlab/ci/jwt_v2.rb @@ -9,27 +9,27 @@ class JwtV2 < Jwt GITLAB_HOSTED_RUNNER = 'gitlab-hosted' SELF_HOSTED_RUNNER = 'self-hosted' - def self.for_build(build, aud: DEFAULT_AUD, wlif: nil) - new(build, ttl: build.metadata_timeout, aud: aud, wlif: wlif).encoded + def self.for_build(build, aud: DEFAULT_AUD, target_audience: nil) + new(build, ttl: build.metadata_timeout, aud: aud, target_audience: target_audience).encoded end - def initialize(build, ttl:, aud:, wlif:) + def initialize(build, ttl:, aud:, target_audience:) super(build, ttl: ttl) @aud = aud - @wlif = wlif + @target_audience = target_audience end private - attr_reader :aud, :wlif + attr_reader :aud, :target_audience def reserved_claims super.merge({ iss: Gitlab.config.gitlab.url, sub: "project_path:#{project.full_path}:ref_type:#{ref_type}:ref:#{source_ref}", aud: aud, - wlif: wlif + target_audience: target_audience }.compact) end diff --git a/spec/lib/gitlab/ci/jwt_v2_spec.rb b/spec/lib/gitlab/ci/jwt_v2_spec.rb index e53975b617c875522e2f5960d1bce96b497e657d..df2b216143162ae91e798a2aa29366b74aee8552 100644 --- a/spec/lib/gitlab/ci/jwt_v2_spec.rb +++ b/spec/lib/gitlab/ci/jwt_v2_spec.rb @@ -15,7 +15,7 @@ let(:pipeline) { build_stubbed(:ci_pipeline, ref: 'auto-deploy-2020-03-19') } let(:runner) { build_stubbed(:ci_runner) } let(:aud) { described_class::DEFAULT_AUD } - let(:wlif) { nil } + let(:target_audience) { nil } let(:build) do build_stubbed( @@ -27,7 +27,7 @@ ) end - subject(:ci_job_jwt_v2) { described_class.new(build, ttl: 30, aud: aud, wlif: wlif) } + subject(:ci_job_jwt_v2) { described_class.new(build, ttl: 30, aud: aud, target_audience: target_audience) } it { is_expected.to be_a Gitlab::Ci::Jwt } @@ -61,16 +61,16 @@ expect(payload[:aud]).to eq('AWS') end - it 'does not use wlif claim in the payload' do - expect(payload.include?(:wlif)).to be_falsey + it 'does not use target_audience claim in the payload' do + expect(payload.include?(:target_audience)).to be_falsey end end - context 'when given an wlif claim' do - let(:wlif) { '//iam.googleapis.com/foo' } + context 'when given an target_audience claim' do + let(:target_audience) { '//iam.googleapis.com/foo' } - it 'uses specified wlif in the payload' do - expect(payload[:wlif]).to eq(wlif) + it 'uses specified target_audience in the payload' do + expect(payload[:target_audience]).to eq(target_audience) end end