Skip to content
代码片段 群组 项目
未验证 提交 35d18c4d 编辑于 作者: Pedro Pombeiro's avatar Pedro Pombeiro 提交者: GitLab
浏览文件

Merge branch 'pedropombeiro/441421/allow-specifying-gc-project' into 'master'

No related branches found
No related tags found
无相关合并请求
显示
254 个添加185 个删除
......@@ -11,6 +11,9 @@
"AuditEventStreamingHeader",
"AuditEventsStreamingInstanceHeader"
],
"CiRunnerCloudProvisioningOptions": [
"CiRunnerGoogleCloudProvisioningOptions"
],
"CiVariable": [
"CiGroupVariable",
"CiInstanceVariable",
......
......@@ -16443,19 +16443,41 @@ Machine type used for runner cloud provisioning.
| <a id="cirunnercloudprovisioningmachinetypename"></a>`name` | [`String`](#string) | Name of the machine type. |
| <a id="cirunnercloudprovisioningmachinetypezone"></a>`zone` | [`String`](#string) | Zone of the machine type. |
 
### `CiRunnerCloudProvisioningOptions`
### `CiRunnerCloudProvisioningRegion`
 
Options for runner cloud provisioning.
Region used for runner cloud provisioning.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cirunnercloudprovisioningregiondescription"></a>`description` | [`String`](#string) | Description of the region. |
| <a id="cirunnercloudprovisioningregionname"></a>`name` | [`String`](#string) | Name of the region. |
### `CiRunnerCloudProvisioningZone`
Zone used for runner cloud provisioning.
 
#### Fields
 
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cirunnercloudprovisioningoptionsregions"></a>`regions` | [`CiRunnerCloudProvisioningRegionConnection`](#cirunnercloudprovisioningregionconnection) | Regions available for provisioning a runner. (see [Connections](#connections)) |
| <a id="cirunnercloudprovisioningzonedescription"></a>`description` | [`String`](#string) | Description of the zone. |
| <a id="cirunnercloudprovisioningzonename"></a>`name` | [`String`](#string) | Name of the zone. |
### `CiRunnerGoogleCloudProvisioningOptions`
Options for runner Google Cloud provisioning.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cirunnergooglecloudprovisioningoptionsregions"></a>`regions` | [`CiRunnerCloudProvisioningRegionConnection`](#cirunnercloudprovisioningregionconnection) | Regions available for provisioning a runner. (see [Connections](#connections)) |
 
#### Fields with arguments
 
##### `CiRunnerCloudProvisioningOptions.machineTypes`
##### `CiRunnerGoogleCloudProvisioningOptions.machineTypes`
 
Machine types available for provisioning a runner.
 
......@@ -16469,9 +16491,9 @@ four standard [pagination arguments](#connection-pagination-arguments):
 
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cirunnercloudprovisioningoptionsmachinetypeszone"></a>`zone` | [`String!`](#string) | Zone for which to retrieve machine types. |
| <a id="cirunnergooglecloudprovisioningoptionsmachinetypeszone"></a>`zone` | [`String!`](#string) | Zone to retrieve machine types for. |
 
##### `CiRunnerCloudProvisioningOptions.zones`
##### `CiRunnerGoogleCloudProvisioningOptions.zones`
 
Zones available for provisioning a runner.
 
......@@ -16485,29 +16507,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
 
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cirunnercloudprovisioningoptionszonesregion"></a>`region` | [`String`](#string) | Region for which to retrieve zones. Returns all zones if not specified. |
### `CiRunnerCloudProvisioningRegion`
Region used for runner cloud provisioning.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cirunnercloudprovisioningregiondescription"></a>`description` | [`String`](#string) | Description of the region. |
| <a id="cirunnercloudprovisioningregionname"></a>`name` | [`String`](#string) | Name of the region. |
### `CiRunnerCloudProvisioningZone`
Zone used for runner cloud provisioning.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cirunnercloudprovisioningzonedescription"></a>`description` | [`String`](#string) | Description of the zone. |
| <a id="cirunnercloudprovisioningzonename"></a>`name` | [`String`](#string) | Name of the zone. |
| <a id="cirunnergooglecloudprovisioningoptionszonesregion"></a>`region` | [`String`](#string) | Region to retrieve zones for. Returns all zones if not specified. |
 
### `CiRunnerManager`
 
......@@ -26273,7 +26273,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
 
##### `Project.runnerCloudProvisioningOptions`
 
Options for runner cloud provisioning by a specified cloud provider. Returns `null` if `:google_cloud_runner_provisioning` feature flag is disabled, or the GitLab instance is not a SaaS instance.
Options for provisioning the runner on Google Cloud. Returns `null` if `:google_cloud_runner_provisioning` feature flag is disabled, or the GitLab instance is not a SaaS instance.
 
NOTE:
**Introduced** in 16.9.
......@@ -26285,6 +26285,7 @@ Returns [`CiRunnerCloudProvisioningOptions`](#cirunnercloudprovisioningoptions).
 
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectrunnercloudprovisioningoptionscloudprojectid"></a>`cloudProjectId` | [`String!`](#string) | Identifier of the cloud project. |
| <a id="projectrunnercloudprovisioningoptionsprovider"></a>`provider` | [`CiRunnerCloudProvider!`](#cirunnercloudprovider) | Identifier of the cloud provider. |
 
##### `Project.runners`
......@@ -34014,6 +34015,14 @@ abstract types.
 
### Unions
 
#### `CiRunnerCloudProvisioningOptions`
Options for runner cloud provisioning.
One of:
- [`CiRunnerGoogleCloudProvisioningOptions`](#cirunnergooglecloudprovisioningoptions)
#### `DependencyLinkMetadata`
 
Represents metadata associated with a dependency link.
......@@ -342,11 +342,13 @@ module ProjectType
::Types::Ci::RunnerCloudProvisioningOptionsType,
null: true,
alpha: { milestone: '16.9' },
description: 'Options for runner cloud provisioning by a specified cloud provider. ' \
description: 'Options for provisioning the runner on Google Cloud. ' \
'Returns `null` if `:google_cloud_runner_provisioning` feature flag is disabled, ' \
'or the GitLab instance is not a SaaS instance.' do
argument :provider, ::Types::Ci::RunnerCloudProviderEnum, required: true,
description: 'Identifier of the cloud provider.'
argument :cloud_project_id, GraphQL::Types::String, required: true,
description: 'Identifier of the cloud project.'
end
field :ai_agents, ::Types::Ai::Agents::AgentType.connection_type,
......@@ -406,14 +408,14 @@ def compliance_frameworks
end
end
# TODO To be removed along with :google_cloud_runner_provisioning feature flag.
# Use `method: :itself` on the related field (see https://graphql-ruby.org/fields/introduction.html#field-resolution).
# TODO Before unmarking the field as alpha, figure out solution for polymorphism based on provider argument,
# so that child objects call the correct cloud services
def runner_cloud_provisioning_options(provider:) # rubocop:disable Lint/UnusedMethodArgument -- Only one provider type is possible, and is already enforced by GraphQL
def runner_cloud_provisioning_options(provider:, cloud_project_id:)
return if ::Feature.disabled?(:google_cloud_runner_provisioning, project)
project
{
project: project,
provider: provider,
cloud_project_id: cloud_project_id
}
end
def google_cloud_artifact_registry_repository
......
# frozen_string_literal: true
module Resolvers
module Ci
# rubocop: disable Graphql/ResolverType -- the type is decided on the derived resolver class
class RunnerCloudProvisioningBaseResolver < BaseResolver
include Gitlab::Graphql::Authorize::AuthorizeResource
authorize :read_runner_cloud_provisioning_options
private
alias_method :project, :object
def default_params(after, first)
{ max_results: first, page_token: after }.compact
end
def externally_paginated_array(response, after)
raise_resource_not_available_error!(response.message) if response.error?
Gitlab::Graphql::ExternallyPaginatedArray.new(
after,
response.payload[:next_page_token],
*response.payload[:items]
)
end
end
# rubocop: enable Graphql/ResolverType
end
end
# frozen_string_literal: true
module Resolvers
module Ci
class RunnerCloudProvisioningMachineTypesResolver < Resolvers::Ci::RunnerCloudProvisioningBaseResolver
type Types::Ci::RunnerCloudProvisioningMachineTypeType.connection_type, null: true
description 'Machine types available for provisioning a runner.'
argument :zone, GraphQL::Types::String,
required: true,
description: 'Zone for which to retrieve machine types.'
max_page_size GoogleCloudPlatform::Compute::ListMachineTypesService::MAX_RESULTS_LIMIT
default_page_size GoogleCloudPlatform::Compute::ListMachineTypesService::MAX_RESULTS_LIMIT
def resolve(zone:, after: nil, first: nil)
response = GoogleCloudPlatform::Compute::ListMachineTypesService
.new(project: project, current_user: current_user, zone: zone, params: default_params(after, first))
.execute
externally_paginated_array(response, after)
end
end
end
end
# frozen_string_literal: true
module Resolvers
module Ci
class RunnerCloudProvisioningRegionsResolver < Resolvers::Ci::RunnerCloudProvisioningBaseResolver
type Types::Ci::RunnerCloudProvisioningRegionType.connection_type, null: true
description 'Regions available for provisioning a runner.'
max_page_size GoogleCloudPlatform::Compute::ListRegionsService::MAX_RESULTS_LIMIT
default_page_size GoogleCloudPlatform::Compute::ListRegionsService::MAX_RESULTS_LIMIT
def resolve(after: nil, first: nil)
response = GoogleCloudPlatform::Compute::ListRegionsService
.new(project: project, current_user: current_user, params: default_params(after, first))
.execute
externally_paginated_array(response, after)
end
end
end
end
# frozen_string_literal: true
module Resolvers
module Ci
class RunnerCloudProvisioningZonesResolver < Resolvers::Ci::RunnerCloudProvisioningBaseResolver
type Types::Ci::RunnerCloudProvisioningZoneType.connection_type, null: true
description 'Zones available for provisioning a runner.'
argument :region, GraphQL::Types::String,
required: false,
description: 'Region for which to retrieve zones. Returns all zones if not specified.'
max_page_size GoogleCloudPlatform::Compute::ListZonesService::MAX_RESULTS_LIMIT
default_page_size GoogleCloudPlatform::Compute::ListZonesService::MAX_RESULTS_LIMIT
def resolve(region: nil, after: nil, first: nil)
params = default_params(after, first)
params[:filter] = "name=#{region}-*" if region
response = GoogleCloudPlatform::Compute::ListZonesService
.new(project: project, current_user: current_user, params: params)
.execute
externally_paginated_array(response, after)
end
end
end
end
......@@ -2,29 +2,22 @@
module Types
module Ci
class RunnerCloudProvisioningOptionsType < BaseObject
class RunnerCloudProvisioningOptionsType < BaseUnion
graphql_name 'CiRunnerCloudProvisioningOptions'
description 'Options for runner cloud provisioning.'
include Gitlab::Graphql::Authorize::AuthorizeResource
UnexpectedProviderType = Class.new(StandardError)
authorize :read_runner_cloud_provisioning_options
possible_types ::Types::Ci::RunnerGoogleCloudProvisioningOptionsType
field :regions, Types::Ci::RunnerCloudProvisioningRegionType.connection_type,
null: true,
resolver: ::Resolvers::Ci::RunnerCloudProvisioningRegionsResolver,
connection_extension: Gitlab::Graphql::Extensions::ForwardOnlyExternallyPaginatedArrayExtension
field :zones, Types::Ci::RunnerCloudProvisioningZoneType.connection_type,
null: true,
resolver: ::Resolvers::Ci::RunnerCloudProvisioningZonesResolver,
connection_extension: Gitlab::Graphql::Extensions::ForwardOnlyExternallyPaginatedArrayExtension
field :machine_types,
Types::Ci::RunnerCloudProvisioningMachineTypeType.connection_type,
null: true,
resolver: ::Resolvers::Ci::RunnerCloudProvisioningMachineTypesResolver,
connection_extension: Gitlab::Graphql::Extensions::ForwardOnlyExternallyPaginatedArrayExtension
def self.resolve_type(object, _context)
case object[:provider]
when :google_cloud
::Types::Ci::RunnerGoogleCloudProvisioningOptionsType
else
raise UnexpectedProviderType, 'Unsupported CI runner cloud provider'
end
end
end
end
end
# frozen_string_literal: true
module Types
module Ci
class RunnerGoogleCloudProvisioningOptionsType < BaseObject
graphql_name 'CiRunnerGoogleCloudProvisioningOptions'
description 'Options for runner Google Cloud provisioning.'
include Gitlab::Graphql::Authorize::AuthorizeResource
authorize :read_runner_cloud_provisioning_options
field :regions, Types::Ci::RunnerCloudProvisioningRegionType.connection_type,
description: 'Regions available for provisioning a runner.',
null: true,
connection_extension: Gitlab::Graphql::Extensions::ForwardOnlyExternallyPaginatedArrayExtension,
max_page_size: GoogleCloudPlatform::Compute::ListRegionsService::MAX_RESULTS_LIMIT,
default_page_size: GoogleCloudPlatform::Compute::ListRegionsService::MAX_RESULTS_LIMIT
field :zones, Types::Ci::RunnerCloudProvisioningZoneType.connection_type,
description: 'Zones available for provisioning a runner.',
null: true,
connection_extension: Gitlab::Graphql::Extensions::ForwardOnlyExternallyPaginatedArrayExtension,
max_page_size: GoogleCloudPlatform::Compute::ListZonesService::MAX_RESULTS_LIMIT,
default_page_size: GoogleCloudPlatform::Compute::ListZonesService::MAX_RESULTS_LIMIT do
argument :region, GraphQL::Types::String, required: false,
description: 'Region to retrieve zones for. Returns all zones if not specified.'
end
field :machine_types,
Types::Ci::RunnerCloudProvisioningMachineTypeType.connection_type,
description: 'Machine types available for provisioning a runner.',
null: true,
connection_extension: Gitlab::Graphql::Extensions::ForwardOnlyExternallyPaginatedArrayExtension,
max_page_size: GoogleCloudPlatform::Compute::ListMachineTypesService::MAX_RESULTS_LIMIT,
default_page_size: GoogleCloudPlatform::Compute::ListMachineTypesService::MAX_RESULTS_LIMIT do
argument :zone, GraphQL::Types::String, required: true, description: 'Zone to retrieve machine types for.'
end
def self.authorized?(object, context)
super(object[:project], context)
end
def regions(after: nil, first: nil)
response = GoogleCloudPlatform::Compute::ListRegionsService
.new(project: project, current_user: current_user,
params: default_params(after, first).merge(google_cloud_project_id: google_cloud_project_id))
.execute
externally_paginated_array(response, after)
end
def zones(region: nil, after: nil, first: nil)
params = default_params(after, first)
params[:filter] = "name=#{region}-*" if region
params[:google_cloud_project_id] = google_cloud_project_id if google_cloud_project_id
response = GoogleCloudPlatform::Compute::ListZonesService
.new(project: project, current_user: current_user, params: params)
.execute
externally_paginated_array(response, after)
end
def machine_types(zone:, after: nil, first: nil)
response = GoogleCloudPlatform::Compute::ListMachineTypesService
.new(
project: project, current_user: current_user, zone: zone,
params: default_params(after, first).merge(google_cloud_project_id: google_cloud_project_id)
)
.execute
externally_paginated_array(response, after)
end
private
def project
object[:project]
end
def google_cloud_project_id
object[:cloud_project_id]
end
def default_params(after, first)
{ max_results: first, page_token: after }.compact
end
def externally_paginated_array(response, after)
raise_resource_not_available_error!(response.message) if response.error?
Gitlab::Graphql::ExternallyPaginatedArray.new(
after,
response.payload[:next_page_token],
*response.payload[:items]
)
end
end
end
end
......@@ -78,7 +78,7 @@ def client
end
def gcp_project_id
project_integration.artifact_registry_project_id
params[:google_cloud_project_id] || project_integration.artifact_registry_project_id
end
def gcp_wlif
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['CiRunnerCloudProvisioningOptions'], feature_category: :fleet_visibility do
it 'returns all possible types' do
expect(described_class.possible_types).to include(
::Types::Ci::RunnerGoogleCloudProvisioningOptionsType
)
end
describe '#resolve_type' do
using RSpec::Parameterized::TableSyntax
where(:provider, :expected_type) do
:google_cloud | ::Types::Ci::RunnerGoogleCloudProvisioningOptionsType
end
subject(:resolved_type) do
described_class.resolve_type({ project: nil, provider: provider, cloud_project_id: 'some_project_id' }, {})
end
with_them do
specify { expect(resolved_type).to eq(expected_type) }
end
context 'when provider is unknown' do
let(:provider) { :unknown }
it 'raises an error' do
expect { resolved_type }.to raise_error(Types::Ci::RunnerCloudProvisioningOptionsType::UnexpectedProviderType)
end
end
end
end
......@@ -29,8 +29,8 @@
security_policy_project security_training_urls vulnerability_images only_allow_merge_if_all_status_checks_passed
security_policy_project_linked_projects security_policy_project_linked_namespaces
dependencies merge_requests_disable_committers_approval has_jira_vulnerability_issue_creation_enabled
ci_subscriptions_projects ci_subscribed_projects ai_agents duo_features_enabled runner_cloud_provisioning_options
google_cloud_artifact_registry_repository
ci_subscriptions_projects ci_subscribed_projects ai_agents duo_features_enabled
runner_cloud_provisioning_options google_cloud_artifact_registry_repository
]
expect(described_class).to include_graphql_fields(*expected_fields)
......@@ -497,12 +497,4 @@
it { is_expected.to have_graphql_type(::Types::Ci::RunnerCloudProvisioningOptionsType) }
end
private
def query_for_project(project)
graphql_query_for(
:projects, { ids: [global_id_of(project)] }, "nodes { #{query_nodes(:compliance_frameworks)} }"
)
end
end
......@@ -7,7 +7,7 @@
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_project_id) { 'cloud_project_id' }
let(:gcp_wlif) { '//wlif.test' }
let(:user) { project.owner }
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'runnerCloudProvisioningOptions', feature_category: :fleet_visibility do
RSpec.describe 'runnerGoogleCloudProvisioningOptions', feature_category: :fleet_visibility do
include GraphqlHelpers
let_it_be_with_refind(:project) { create(:project) }
......@@ -11,11 +11,12 @@
let(:client_klass) { GoogleCloudPlatform::Compute::Client }
let(:current_user) { maintainer }
let(:google_cloud_project_id) { 'project_id_override' }
let(:expected_compute_client_args) do
{
project: project,
user: current_user,
gcp_project_id: integration.artifact_registry_project_id,
gcp_project_id: google_cloud_project_id,
gcp_wlif: integration.wlif
}
end
......@@ -30,9 +31,14 @@
graphql_query_for(
:project, { fullPath: project.full_path },
query_graphql_field(
:runner_cloud_provisioning_options, { provider: :GOOGLE_CLOUD },
query_nodes(node_name, args: base_item_query_args.merge(item_query_args), of: item_type,
include_pagination_info: true)
:runner_cloud_provisioning_options, { provider: :GOOGLE_CLOUD, cloud_project_id: google_cloud_project_id },
"... on CiRunnerGoogleCloudProvisioningOptions {
#{query_nodes(
node_name,
args: base_item_query_args.merge(item_query_args),
of: item_type,
include_pagination_info: true)}
}"
)
)
end
......
......@@ -14,7 +14,10 @@
let(:order_by) { 'name asc' }
let(:service) { described_class.new(project: project, current_user: user, zone: zone, params: params) }
let(:params) do
{ filter: filter, max_results: max_results, page_token: page_token, order_by: order_by }
{
google_cloud_project_id: google_cloud_project_id, filter: filter,
max_results: max_results, page_token: page_token, order_by: order_by
}.compact
end
subject(:response) { service.execute }
......@@ -30,6 +33,8 @@
.and_return(dummy_list_response)
end
it_behaves_like 'overriding the google cloud project id'
it 'returns the machine_types' do
expect(response).to be_success
expect(response.payload[:items]).to be_a Enumerable
......
......@@ -12,7 +12,10 @@
let(:page_token) { 'token' }
let(:order_by) { 'name asc' }
let(:params) do
{ filter: filter, max_results: max_results, page_token: page_token, order_by: order_by }
{
google_cloud_project_id: google_cloud_project_id, filter: filter,
max_results: max_results, page_token: page_token, order_by: order_by
}.compact
end
subject(:response) { service.execute }
......@@ -35,6 +38,8 @@
expect(response.payload[:next_page_token]).to eq('next_page_token')
end
it_behaves_like 'overriding the google cloud project id'
context 'with an invalid order_by' do
where(:field, :direction) do
'test' | 'asc'
......
......@@ -12,7 +12,10 @@
let(:page_token) { 'token' }
let(:order_by) { 'name asc' }
let(:params) do
{ filter: filter, max_results: max_results, page_token: page_token, order_by: order_by }
{
google_cloud_project_id: google_cloud_project_id, filter: filter,
max_results: max_results, page_token: page_token, order_by: order_by
}.compact
end
subject(:response) { service.execute }
......@@ -35,6 +38,8 @@
expect(response.payload[:next_page_token]).to eq('next_page_token')
end
it_behaves_like 'overriding the google cloud project id'
context 'with an invalid order_by' do
where(:field, :direction) do
'test' | 'asc'
......
......@@ -6,7 +6,7 @@
create(
:google_cloud_platform_artifact_registry_integration,
project: project,
artifact_registry_project_id: 'gcp_project_id',
artifact_registry_project_id: 'cloud_project_id',
workload_identity_pool_project_number: '555',
workload_identity_pool_id: 'my_pool',
workload_identity_pool_provider_id: 'my_provider'
......@@ -16,13 +16,14 @@
let(:user) { project.owner }
let(:service) { described_class.new(project: project, current_user: user, params: params) }
let(:client_double) { instance_double('::GoogleCloudPlatform::Compute::Client') }
let(:google_cloud_project_id) { nil }
before do
allow(::GoogleCloudPlatform::Compute::Client).to receive(:new)
.with(
project: project,
user: user,
gcp_project_id: project_integration.artifact_registry_project_id,
gcp_project_id: google_cloud_project_id || project_integration.artifact_registry_project_id,
gcp_wlif: project_integration.wlif
).and_return(client_double)
end
......
......@@ -67,3 +67,23 @@
end
end
end
RSpec.shared_examples 'overriding the google cloud project id' do
let(:google_cloud_project_id) { 'project_id_override' }
let(:extra_params) { { google_cloud_project_id: google_cloud_project_id } }
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)
expect(response).to be_success
expect(response.payload[:items]).to be_a Enumerable
expect(response.payload[:items].count).to be 1
expect(response.payload[:next_page_token]).to eq('next_page_token')
end
end
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册