Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
fa4a0663e8
commit
d6ce16a407
50 changed files with 603 additions and 143 deletions
|
@ -1 +1 @@
|
||||||
b7f0c0462a8f689c8ee9e654f0875157b238158b
|
69baea2ed2c593ed570b614f1c311d8ee80a1c33
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
GlTooltipDirective,
|
GlTooltipDirective,
|
||||||
GlSafeHtmlDirective,
|
GlSafeHtmlDirective,
|
||||||
GlIcon,
|
GlIcon,
|
||||||
|
GlBadge,
|
||||||
GlButton,
|
GlButton,
|
||||||
GlButtonGroup,
|
GlButtonGroup,
|
||||||
GlDropdown,
|
GlDropdown,
|
||||||
|
@ -34,6 +35,7 @@ export default {
|
||||||
GlIcon,
|
GlIcon,
|
||||||
FileIcon,
|
FileIcon,
|
||||||
DiffStats,
|
DiffStats,
|
||||||
|
GlBadge,
|
||||||
GlButton,
|
GlButton,
|
||||||
GlButtonGroup,
|
GlButtonGroup,
|
||||||
GlDropdown,
|
GlDropdown,
|
||||||
|
@ -349,7 +351,9 @@ export default {
|
||||||
{{ diffFile.a_mode }} → {{ diffFile.b_mode }}
|
{{ diffFile.a_mode }} → {{ diffFile.b_mode }}
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
<span v-if="isUsingLfs" class="badge label label-lfs gl-mr-2"> {{ __('LFS') }} </span>
|
<gl-badge v-if="isUsingLfs" variant="neutral" class="gl-mr-2" data-testid="label-lfs">{{
|
||||||
|
__('LFS')
|
||||||
|
}}</gl-badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -267,6 +267,8 @@
|
||||||
|
|
||||||
.nav-item-name {
|
.nav-item-name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
> a,
|
> a,
|
||||||
|
|
|
@ -411,11 +411,6 @@ span.idiff {
|
||||||
margin-right: 1.5em;
|
margin-right: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-lfs {
|
|
||||||
color: $common-gray-light;
|
|
||||||
border: 1px solid $common-gray-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-container {
|
.preview-container {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
|
|
|
@ -1095,6 +1095,8 @@ input {
|
||||||
}
|
}
|
||||||
.nav-sidebar li .nav-item-name {
|
.nav-sidebar li .nav-item-name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
.nav-sidebar li > a,
|
.nav-sidebar li > a,
|
||||||
.nav-sidebar li > .fly-out-top-item-container {
|
.nav-sidebar li > .fly-out-top-item-container {
|
||||||
|
|
|
@ -1076,6 +1076,8 @@ input {
|
||||||
}
|
}
|
||||||
.nav-sidebar li .nav-item-name {
|
.nav-sidebar li .nav-item-name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
.nav-sidebar li > a,
|
.nav-sidebar li > a,
|
||||||
.nav-sidebar li > .fly-out-top-item-container {
|
.nav-sidebar li > .fly-out-top-item-container {
|
||||||
|
|
|
@ -28,7 +28,12 @@ module Mutations
|
||||||
|
|
||||||
argument :active, GraphQL::Types::Boolean,
|
argument :active, GraphQL::Types::Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
description: 'Indicates the runner is allowed to receive jobs.'
|
description: 'Indicates the runner is allowed to receive jobs.',
|
||||||
|
deprecated: { reason: :renamed, replacement: 'paused', milestone: '14.8' }
|
||||||
|
|
||||||
|
argument :paused, GraphQL::Types::Boolean,
|
||||||
|
required: false,
|
||||||
|
description: 'Indicates the runner is not allowed to receive jobs.'
|
||||||
|
|
||||||
argument :locked, GraphQL::Types::Boolean, required: false,
|
argument :locked, GraphQL::Types::Boolean, required: false,
|
||||||
description: 'Indicates the runner is locked.'
|
description: 'Indicates the runner is locked.'
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
module Ci
|
module Ci
|
||||||
class PipelineScheduleService < BaseService
|
class PipelineScheduleService < BaseService
|
||||||
def execute(schedule)
|
def execute(schedule)
|
||||||
|
return unless project.persisted?
|
||||||
|
|
||||||
# Ensure `next_run_at` is set properly before creating a pipeline.
|
# Ensure `next_run_at` is set properly before creating a pipeline.
|
||||||
# Otherwise, multiple pipelines could be created in a short interval.
|
# Otherwise, multiple pipelines could be created in a short interval.
|
||||||
schedule.schedule_next_run!
|
schedule.schedule_next_run!
|
||||||
|
|
|
@ -9,6 +9,8 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(params)
|
def update(params)
|
||||||
|
params[:active] = !params.delete(:paused) if params.include?(:paused)
|
||||||
|
|
||||||
runner.update(params).tap do |updated|
|
runner.update(params).tap do |updated|
|
||||||
runner.tick_runner_queue if updated
|
runner.tick_runner_queue if updated
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ module GoogleCloud
|
||||||
def execute
|
def execute
|
||||||
service_account = google_api_client.create_service_account(gcp_project_id, service_account_name, service_account_desc)
|
service_account = google_api_client.create_service_account(gcp_project_id, service_account_name, service_account_desc)
|
||||||
service_account_key = google_api_client.create_service_account_key(gcp_project_id, service_account.unique_id)
|
service_account_key = google_api_client.create_service_account_key(gcp_project_id, service_account.unique_id)
|
||||||
|
google_api_client.grant_service_account_roles(gcp_project_id, service_account.email)
|
||||||
|
|
||||||
service_accounts_service.add_for_project(
|
service_accounts_service.add_for_project(
|
||||||
environment_name,
|
environment_name,
|
||||||
|
@ -35,7 +36,7 @@ module GoogleCloud
|
||||||
end
|
end
|
||||||
|
|
||||||
def google_api_client
|
def google_api_client
|
||||||
GoogleApi::CloudPlatform::Client.new(google_oauth2_token, nil)
|
@google_api_client_instance ||= GoogleApi::CloudPlatform::Client.new(google_oauth2_token, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def service_accounts_service
|
def service_accounts_service
|
||||||
|
@ -50,7 +51,7 @@ module GoogleCloud
|
||||||
"GitLab generated service account for project '#{project.name}' and environment '#{environment_name}'"
|
"GitLab generated service account for project '#{project.name}' and environment '#{environment_name}'"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Overriden in EE
|
# Overridden in EE
|
||||||
def environment_protected?
|
def environment_protected?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,4 +14,4 @@
|
||||||
= number_to_human_size(blob.raw_size)
|
= number_to_human_size(blob.raw_size)
|
||||||
|
|
||||||
- if blob.stored_externally? && blob.external_storage == :lfs
|
- if blob.stored_externally? && blob.external_storage == :lfs
|
||||||
%span.badge.label-lfs.gl-mr-2 LFS
|
= gl_badge_tag(_('LFS'), variant: :neutral)
|
||||||
|
|
|
@ -22,13 +22,7 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
def max_running_jobs
|
def max_running_jobs
|
||||||
if ::Feature.enabled?(:ci_delete_objects_medium_concurrency)
|
20
|
||||||
20
|
|
||||||
elsif ::Feature.enabled?(:ci_delete_objects_high_concurrency)
|
|
||||||
50
|
|
||||||
else
|
|
||||||
2
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -13,6 +13,8 @@ class PipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
def perform
|
def perform
|
||||||
Ci::PipelineSchedule.runnable_schedules.preloaded.find_in_batches do |schedules|
|
Ci::PipelineSchedule.runnable_schedules.preloaded.find_in_batches do |schedules|
|
||||||
schedules.each do |schedule|
|
schedules.each do |schedule|
|
||||||
|
next unless schedule.project
|
||||||
|
|
||||||
with_context(project: schedule.project, user: schedule.owner) do
|
with_context(project: schedule.project, user: schedule.owner) do
|
||||||
Ci::PipelineScheduleService.new(schedule.project, schedule.owner).execute(schedule)
|
Ci::PipelineScheduleService.new(schedule.project, schedule.owner).execute(schedule)
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
schedule = Ci::PipelineSchedule.find_by_id(schedule_id)
|
schedule = Ci::PipelineSchedule.find_by_id(schedule_id)
|
||||||
user = User.find_by_id(user_id)
|
user = User.find_by_id(user_id)
|
||||||
|
|
||||||
return unless schedule && user
|
return unless schedule && schedule.project && user
|
||||||
|
|
||||||
run_pipeline_schedule(schedule, user)
|
run_pipeline_schedule(schedule, user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
name: ci_delete_objects_medium_concurrency
|
name: project_import_schedule_worker_job_tracker
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39464
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79097
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/247103
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351408
|
||||||
milestone: '13.5'
|
milestone: '14.8'
|
||||||
type: development
|
type: development
|
||||||
group: group::pipeline execution
|
group: group::scalability
|
||||||
default_enabled: false
|
default_enabled: false
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
name: ci_delete_objects_high_concurrency
|
name: update_all_mirrors_job_tracker
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39464
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79097
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/247103
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351420
|
||||||
milestone: '13.5'
|
milestone: '14.8'
|
||||||
type: development
|
type: development
|
||||||
group: group::pipeline execution
|
group: group::scalability
|
||||||
default_enabled: false
|
default_enabled: false
|
|
@ -1,4 +1,4 @@
|
||||||
- name: "REST API Runner will not contain `paused`"
|
- name: "REST and GraphQL API Runner status will not return `paused`"
|
||||||
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
|
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
|
||||||
announcement_date: "2021-11-22"
|
announcement_date: "2021-11-22"
|
||||||
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
|
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
`online`, `offline`, or `not_connected`. Status `paused` or `active` will no longer appear.
|
`online`, `offline`, or `not_connected`. Status `paused` or `active` will no longer appear.
|
||||||
|
|
||||||
When checking if a runner is `paused`, API users are advised to check the boolean attribute
|
When checking if a runner is `paused`, API users are advised to check the boolean attribute
|
||||||
`active` to be `false` instead. When checking if a runner is `active`, check if `active` is `true`.
|
`paused` to be `true` instead. When checking if a runner is `active`, check if `paused` is `false`.
|
||||||
stage: Verify
|
stage: Verify
|
||||||
tiers: [Core, Premium, Ultimate]
|
tiers: [Core, Premium, Ultimate]
|
||||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344648
|
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344648
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
- name: "REST and GraphQL API Runner usage of `active` replaced by `paused`"
|
||||||
|
announcement_milestone: "14.8"
|
||||||
|
announcement_date: "2022-02-22"
|
||||||
|
removal_milestone: "15.0"
|
||||||
|
removal_date: "2022-05-22"
|
||||||
|
breaking_change: true
|
||||||
|
reporter: pedropombeiro
|
||||||
|
body: |
|
||||||
|
Occurrences of the `active` identifier in the GitLab Runner REST and GraphQL API endpoints will be
|
||||||
|
renamed to `paused` in GitLab 15.0, namely:
|
||||||
|
|
||||||
|
- GraphQL API:
|
||||||
|
- the `CiRunner` property;
|
||||||
|
- the `RunnerUpdateInput` input type for the `runnerUpdate` mutation;
|
||||||
|
- the `runners` and `Group.runners` queries.
|
||||||
|
- REST API:
|
||||||
|
- endpoints taking or returning `active` properties, such as:
|
||||||
|
- `GET /runners`
|
||||||
|
- `GET /runners/all`
|
||||||
|
- `GET /runners/:id` / `PUT /runners/:id`
|
||||||
|
- `PUT --form "active=false" /runners/:runner_id`
|
||||||
|
- `GET /projects/:id/runners` / `POST /projects/:id/runners`
|
||||||
|
- `GET /groups/:id/runners`
|
||||||
|
|
||||||
|
The 15.0 release of the GitLab Runner will start using the `paused` property when registering runners, and therefore
|
||||||
|
will only be compatible with GitLab 15.0 and later. Until 15.0, GitLab will accept the deprecated `active` flag from
|
||||||
|
existing runners.
|
||||||
|
stage: Verify
|
||||||
|
tiers: [Core, Premium, Ultimate]
|
||||||
|
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347211
|
||||||
|
documentation_url: https://docs.gitlab.com/ee/api/runners.html
|
||||||
|
image_url: # (optional) This is a link to a thumbnail image depicting the feature
|
||||||
|
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
|
71
db/post_migrate/20220124130028_dedup_runner_projects.rb
Normal file
71
db/post_migrate/20220124130028_dedup_runner_projects.rb
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DedupRunnerProjects < Gitlab::Database::Migration[1.0]
|
||||||
|
TABLE_NAME = :ci_runner_projects
|
||||||
|
TMP_INDEX_NAME = 'tmp_unique_ci_runner_projects_by_runner_id_and_project_id'
|
||||||
|
OLD_INDEX_NAME = 'index_ci_runner_projects_on_runner_id_and_project_id'
|
||||||
|
INDEX_NAME = 'index_unique_ci_runner_projects_on_runner_id_and_project_id'
|
||||||
|
BATCH_SIZE = 5000
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
module Ci
|
||||||
|
class RunnerProject < ActiveRecord::Base
|
||||||
|
include EachBatch
|
||||||
|
|
||||||
|
self.table_name = 'ci_runner_projects'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def up
|
||||||
|
last_runner_project_record_id = Ci::RunnerProject.maximum(:id) || 0
|
||||||
|
|
||||||
|
# This index will disallow further duplicates while we're deduplicating the data.
|
||||||
|
add_concurrent_index(TABLE_NAME, [:runner_id, :project_id], where: "id > #{Integer(last_runner_project_record_id)}", unique: true, name: TMP_INDEX_NAME)
|
||||||
|
|
||||||
|
Ci::RunnerProject.each_batch(of: BATCH_SIZE) do |relation|
|
||||||
|
duplicated_runner_projects = Ci::RunnerProject
|
||||||
|
.select('COUNT(*)', :runner_id, :project_id)
|
||||||
|
.where('(runner_id, project_id) IN (?)', relation.select(:runner_id, :project_id))
|
||||||
|
.group(:runner_id, :project_id)
|
||||||
|
.having('COUNT(*) > 1')
|
||||||
|
|
||||||
|
duplicated_runner_projects.each do |runner_project|
|
||||||
|
deduplicate_item(runner_project)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
add_concurrent_index(TABLE_NAME, [:runner_id, :project_id], unique: true, name: INDEX_NAME)
|
||||||
|
remove_concurrent_index_by_name(TABLE_NAME, TMP_INDEX_NAME)
|
||||||
|
remove_concurrent_index_by_name(TABLE_NAME, OLD_INDEX_NAME)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_concurrent_index(TABLE_NAME, [:runner_id, :project_id], name: OLD_INDEX_NAME)
|
||||||
|
remove_concurrent_index_by_name(TABLE_NAME, TMP_INDEX_NAME)
|
||||||
|
remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def deduplicate_item(runner_project)
|
||||||
|
runner_projects_records = Ci::RunnerProject
|
||||||
|
.where(project_id: runner_project.project_id, runner_id: runner_project.runner_id)
|
||||||
|
.order(updated_at: :asc)
|
||||||
|
.to_a
|
||||||
|
|
||||||
|
attributes = {}
|
||||||
|
runner_projects_records.each do |runner_projects_record|
|
||||||
|
params = runner_projects_record.attributes.except('id')
|
||||||
|
attributes.merge!(params.compact)
|
||||||
|
end
|
||||||
|
|
||||||
|
ApplicationRecord.transaction do
|
||||||
|
record_to_keep = runner_projects_records.pop
|
||||||
|
records_to_delete = runner_projects_records
|
||||||
|
|
||||||
|
Ci::RunnerProject.where(id: records_to_delete.map(&:id)).delete_all
|
||||||
|
record_to_keep.update!(attributes)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveProjectsCiSourcesProjectsSourceProjectIdFk < Gitlab::Database::Migration[1.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
return unless foreign_key_exists?(:ci_sources_projects, :projects, name: "fk_rails_64b6855cbc")
|
||||||
|
|
||||||
|
with_lock_retries do
|
||||||
|
execute('LOCK projects, ci_sources_projects IN ACCESS EXCLUSIVE MODE') if transaction_open?
|
||||||
|
|
||||||
|
remove_foreign_key_if_exists(:ci_sources_projects, :projects, name: "fk_rails_64b6855cbc")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_concurrent_foreign_key(:ci_sources_projects, :projects, name: "fk_rails_64b6855cbc", column: :source_project_id, target_column: :id, on_delete: :cascade)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveProjectsCiPipelineSchedulesProjectIdFk < Gitlab::Database::Migration[1.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
return unless foreign_key_exists?(:ci_pipeline_schedules, :projects, name: "fk_8ead60fcc4")
|
||||||
|
|
||||||
|
with_lock_retries do
|
||||||
|
execute('LOCK projects, ci_pipeline_schedules IN ACCESS EXCLUSIVE MODE') if transaction_open?
|
||||||
|
|
||||||
|
remove_foreign_key_if_exists(:ci_pipeline_schedules, :projects, name: "fk_8ead60fcc4")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_concurrent_foreign_key(:ci_pipeline_schedules, :projects, name: "fk_8ead60fcc4", column: :project_id, target_column: :id, on_delete: :cascade)
|
||||||
|
end
|
||||||
|
end
|
1
db/schema_migrations/20220124130028
Normal file
1
db/schema_migrations/20220124130028
Normal file
|
@ -0,0 +1 @@
|
||||||
|
7f2b3e70e33273d75f68bd1fa33103f24a4e4cfc3f2e5777dfd258b5a2e7bf4e
|
1
db/schema_migrations/20220126202654
Normal file
1
db/schema_migrations/20220126202654
Normal file
|
@ -0,0 +1 @@
|
||||||
|
6067e4e22e49548496454b48171f8168f7d5bf626fedab4351e2a37a3f85731b
|
1
db/schema_migrations/20220126203421
Normal file
1
db/schema_migrations/20220126203421
Normal file
|
@ -0,0 +1 @@
|
||||||
|
07f837ddde21e36d1ca6a471dd96350d3020bd30204fca0e093983810c94e54d
|
|
@ -25713,8 +25713,6 @@ CREATE UNIQUE INDEX index_ci_runner_namespaces_on_runner_id_and_namespace_id ON
|
||||||
|
|
||||||
CREATE INDEX index_ci_runner_projects_on_project_id ON ci_runner_projects USING btree (project_id);
|
CREATE INDEX index_ci_runner_projects_on_project_id ON ci_runner_projects USING btree (project_id);
|
||||||
|
|
||||||
CREATE INDEX index_ci_runner_projects_on_runner_id_and_project_id ON ci_runner_projects USING btree (runner_id, project_id);
|
|
||||||
|
|
||||||
CREATE INDEX index_ci_runners_on_active ON ci_runners USING btree (active, id);
|
CREATE INDEX index_ci_runners_on_active ON ci_runners USING btree (active, id);
|
||||||
|
|
||||||
CREATE INDEX index_ci_runners_on_contacted_at_and_id_desc ON ci_runners USING btree (contacted_at, id DESC);
|
CREATE INDEX index_ci_runners_on_contacted_at_and_id_desc ON ci_runners USING btree (contacted_at, id DESC);
|
||||||
|
@ -27797,6 +27795,8 @@ CREATE INDEX index_u2f_registrations_on_user_id ON u2f_registrations USING btree
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_uniq_im_issuable_escalation_statuses_on_issue_id ON incident_management_issuable_escalation_statuses USING btree (issue_id);
|
CREATE UNIQUE INDEX index_uniq_im_issuable_escalation_statuses_on_issue_id ON incident_management_issuable_escalation_statuses USING btree (issue_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX index_unique_ci_runner_projects_on_runner_id_and_project_id ON ci_runner_projects USING btree (runner_id, project_id);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_unique_issue_metrics_issue_id ON issue_metrics USING btree (issue_id);
|
CREATE UNIQUE INDEX index_unique_issue_metrics_issue_id ON issue_metrics USING btree (issue_id);
|
||||||
|
|
||||||
CREATE INDEX index_unit_test_failures_failed_at ON ci_unit_test_failures USING btree (failed_at DESC);
|
CREATE INDEX index_unit_test_failures_failed_at ON ci_unit_test_failures USING btree (failed_at DESC);
|
||||||
|
@ -29580,9 +29580,6 @@ ALTER TABLE ONLY releases
|
||||||
ALTER TABLE ONLY protected_tags
|
ALTER TABLE ONLY protected_tags
|
||||||
ADD CONSTRAINT fk_8e4af87648 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_8e4af87648 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY ci_pipeline_schedules
|
|
||||||
ADD CONSTRAINT fk_8ead60fcc4 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE ONLY todos
|
ALTER TABLE ONLY todos
|
||||||
ADD CONSTRAINT fk_91d1f47b13 FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_91d1f47b13 FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
@ -30606,9 +30603,6 @@ ALTER TABLE ONLY reviews
|
||||||
ALTER TABLE ONLY operations_feature_flags
|
ALTER TABLE ONLY operations_feature_flags
|
||||||
ADD CONSTRAINT fk_rails_648e241be7 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_rails_648e241be7 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY ci_sources_projects
|
|
||||||
ADD CONSTRAINT fk_rails_64b6855cbc FOREIGN KEY (source_project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE ONLY board_group_recent_visits
|
ALTER TABLE ONLY board_group_recent_visits
|
||||||
ADD CONSTRAINT fk_rails_64bfc19bc5 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_rails_64bfc19bc5 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
|
@ -4158,12 +4158,13 @@ Input type: `RunnerUpdateInput`
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---- | ---- | ----------- |
|
| ---- | ---- | ----------- |
|
||||||
| <a id="mutationrunnerupdateaccesslevel"></a>`accessLevel` | [`CiRunnerAccessLevel`](#cirunneraccesslevel) | Access level of the runner. |
|
| <a id="mutationrunnerupdateaccesslevel"></a>`accessLevel` | [`CiRunnerAccessLevel`](#cirunneraccesslevel) | Access level of the runner. |
|
||||||
| <a id="mutationrunnerupdateactive"></a>`active` | [`Boolean`](#boolean) | Indicates the runner is allowed to receive jobs. |
|
| <a id="mutationrunnerupdateactive"></a>`active` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated:** This was renamed. Please use `paused`. Deprecated in 14.8. |
|
||||||
| <a id="mutationrunnerupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
| <a id="mutationrunnerupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||||
| <a id="mutationrunnerupdatedescription"></a>`description` | [`String`](#string) | Description of the runner. |
|
| <a id="mutationrunnerupdatedescription"></a>`description` | [`String`](#string) | Description of the runner. |
|
||||||
| <a id="mutationrunnerupdateid"></a>`id` | [`CiRunnerID!`](#cirunnerid) | ID of the runner to update. |
|
| <a id="mutationrunnerupdateid"></a>`id` | [`CiRunnerID!`](#cirunnerid) | ID of the runner to update. |
|
||||||
| <a id="mutationrunnerupdatelocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. |
|
| <a id="mutationrunnerupdatelocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. |
|
||||||
| <a id="mutationrunnerupdatemaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
|
| <a id="mutationrunnerupdatemaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
|
||||||
|
| <a id="mutationrunnerupdatepaused"></a>`paused` | [`Boolean`](#boolean) | Indicates the runner is not allowed to receive jobs. |
|
||||||
| <a id="mutationrunnerupdateprivateprojectsminutescostfactor"></a>`privateProjectsMinutesCostFactor` | [`Float`](#float) | Private projects' "minutes cost factor" associated with the runner (GitLab.com only). |
|
| <a id="mutationrunnerupdateprivateprojectsminutescostfactor"></a>`privateProjectsMinutesCostFactor` | [`Float`](#float) | Private projects' "minutes cost factor" associated with the runner (GitLab.com only). |
|
||||||
| <a id="mutationrunnerupdatepublicprojectsminutescostfactor"></a>`publicProjectsMinutesCostFactor` | [`Float`](#float) | Public projects' "minutes cost factor" associated with the runner (GitLab.com only). |
|
| <a id="mutationrunnerupdatepublicprojectsminutescostfactor"></a>`publicProjectsMinutesCostFactor` | [`Float`](#float) | Public projects' "minutes cost factor" associated with the runner (GitLab.com only). |
|
||||||
| <a id="mutationrunnerupdaterununtagged"></a>`runUntagged` | [`Boolean`](#boolean) | Indicates the runner is able to run untagged jobs. |
|
| <a id="mutationrunnerupdaterununtagged"></a>`runUntagged` | [`Boolean`](#boolean) | Indicates the runner is able to run untagged jobs. |
|
||||||
|
|
|
@ -54,12 +54,17 @@ GET /runners?tag_list=tag1,tag2
|
||||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners"
|
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The `active` attribute in the response was deprecated [in GitLab 14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/347211).
|
||||||
|
and will be removed in [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/351109). It is replaced by the `paused` attribute.
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"description": "test-1-20150125",
|
"description": "test-1-20150125",
|
||||||
"id": 6,
|
"id": 6,
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
|
@ -71,6 +76,7 @@ Example response:
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"description": "test-2-20150125",
|
"description": "test-2-20150125",
|
||||||
"id": 8,
|
"id": 8,
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
|
@ -107,12 +113,17 @@ GET /runners/all?tag_list=tag1,tag2
|
||||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/all"
|
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/all"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The `active` attribute in the response was deprecated [in GitLab 14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/347211).
|
||||||
|
and will be removed in [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/351109). It is replaced by the `paused` attribute.
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"description": "shared-runner-1",
|
"description": "shared-runner-1",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
|
@ -124,6 +135,7 @@ Example response:
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"description": "shared-runner-2",
|
"description": "shared-runner-2",
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
|
@ -135,6 +147,7 @@ Example response:
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"description": "test-1-20150125",
|
"description": "test-1-20150125",
|
||||||
"id": 6,
|
"id": 6,
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
|
@ -146,6 +159,7 @@ Example response:
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"description": "test-2-20150125",
|
"description": "test-2-20150125",
|
||||||
"id": 8,
|
"id": 8,
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
|
@ -185,11 +199,16 @@ NOTE:
|
||||||
The `token` attribute in the response was deprecated [in GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/issues/214320).
|
The `token` attribute in the response was deprecated [in GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/issues/214320).
|
||||||
and removed in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/214322).
|
and removed in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/214322).
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The `active` attribute in the response was deprecated [in GitLab 14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/347211).
|
||||||
|
and will be removed in [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/351109). It is replaced by the `paused` attribute.
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"architecture": null,
|
"architecture": null,
|
||||||
"description": "test-1-20150125",
|
"description": "test-1-20150125",
|
||||||
"id": 6,
|
"id": 6,
|
||||||
|
@ -229,16 +248,17 @@ Update details of a runner.
|
||||||
PUT /runners/:id
|
PUT /runners/:id
|
||||||
```
|
```
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
|---------------|---------|----------|---------------------|
|
|-------------------|---------|----------|--------------------------------------------------------------------------------------------------|
|
||||||
| `id` | integer | yes | The ID of a runner |
|
| `id` | integer | yes | The ID of a runner |
|
||||||
| `description` | string | no | The description of a runner |
|
| `description` | string | no | The description of a runner |
|
||||||
| `active` | boolean | no | The state of a runner; can be set to `true` or `false` |
|
| `active` | boolean | no | Deprecated: Use `:paused` instead. Flag indicating whether the runner is allowed to receive jobs |
|
||||||
| `tag_list` | array | no | The list of tags for a runner; put array of tags, that should be finally assigned to a runner |
|
| `paused` | boolean | no | Flag indicating whether the runner should ignore new jobs |
|
||||||
| `run_untagged`| boolean | no | Flag indicating the runner can execute untagged jobs |
|
| `tag_list` | array | no | The list of tags for a runner; put array of tags, that should be finally assigned to a runner |
|
||||||
| `locked` | boolean | no | Flag indicating the runner is locked |
|
| `run_untagged` | boolean | no | Flag indicating the runner can execute untagged jobs |
|
||||||
| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
|
| `locked` | boolean | no | Flag indicating the runner is locked |
|
||||||
| `maximum_timeout` | integer | no | Maximum timeout set when this runner handles the job |
|
| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
|
||||||
|
| `maximum_timeout` | integer | no | Maximum timeout set when this runner handles the job |
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/6" \
|
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/6" \
|
||||||
|
@ -249,6 +269,10 @@ NOTE:
|
||||||
The `token` attribute in the response was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/214320) in GitLab 12.10.
|
The `token` attribute in the response was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/214320) in GitLab 12.10.
|
||||||
and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/214322) in GitLab 13.0.
|
and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/214322) in GitLab 13.0.
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The `active` query parameter was deprecated [in GitLab 14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/347211).
|
||||||
|
and will be removed in [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/351109). It is replaced by the `paused` attribute.
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -292,7 +316,12 @@ Example response:
|
||||||
Pause a specific runner.
|
Pause a specific runner.
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
PUT --form "active=false" /runners/:runner_id
|
PUT --form "paused=true" /runners/:runner_id
|
||||||
|
|
||||||
|
# --or--
|
||||||
|
|
||||||
|
# Deprecated: removal planned in 15.0
|
||||||
|
PUT --form "active=false" /runners/:runner_id
|
||||||
```
|
```
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
|
@ -300,10 +329,20 @@ PUT --form "active=false" /runners/:runner_id
|
||||||
| `runner_id` | integer | yes | The ID of a runner |
|
| `runner_id` | integer | yes | The ID of a runner |
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||||
|
--form "paused=true" "https://gitlab.example.com/api/v4/runners/6"
|
||||||
|
|
||||||
|
# --or--
|
||||||
|
|
||||||
|
# Deprecated: removal planned in 15.0
|
||||||
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \
|
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||||
--form "active=false" "https://gitlab.example.com/api/v4/runners/6"
|
--form "active=false" "https://gitlab.example.com/api/v4/runners/6"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The `active` form attribute was deprecated [in GitLab 14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/347211).
|
||||||
|
and will be removed in [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/351109). It is replaced by the `paused` attribute.
|
||||||
|
|
||||||
## List runner's jobs
|
## List runner's jobs
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/15432) in GitLab 10.3.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/15432) in GitLab 10.3.
|
||||||
|
@ -420,12 +459,17 @@ GET /projects/:id/runners?tag_list=tag1,tag2
|
||||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/9/runners"
|
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/9/runners"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The `active` attribute in the response was deprecated [in GitLab 14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/347211).
|
||||||
|
and will be removed in [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/351109). It is replaced by the `paused` attribute.
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"description": "test-2-20150125",
|
"description": "test-2-20150125",
|
||||||
"id": 8,
|
"id": 8,
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
|
@ -437,6 +481,7 @@ Example response:
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"description": "development_runner",
|
"description": "development_runner",
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
|
@ -444,7 +489,7 @@ Example response:
|
||||||
"runner_type": "instance_type",
|
"runner_type": "instance_type",
|
||||||
"name": null,
|
"name": null,
|
||||||
"online": true,
|
"online": true,
|
||||||
"status": "paused"
|
"status": "online"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -525,6 +570,10 @@ GET /groups/:id/runners?tag_list=tag1,tag2
|
||||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/9/runners"
|
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/9/runners"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The `active` attribute in the response was deprecated [in GitLab 14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/347211).
|
||||||
|
and will be removed in [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/351109). It is replaced by the `paused` attribute.
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -534,6 +583,7 @@ Example response:
|
||||||
"description": "Shared",
|
"description": "Shared",
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"is_shared": true,
|
"is_shared": true,
|
||||||
"runner_type": "instance_type",
|
"runner_type": "instance_type",
|
||||||
"name": "gitlab-runner",
|
"name": "gitlab-runner",
|
||||||
|
@ -545,6 +595,7 @@ Example response:
|
||||||
"description": "Test",
|
"description": "Test",
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"is_shared": true,
|
"is_shared": true,
|
||||||
"runner_type": "instance_type",
|
"runner_type": "instance_type",
|
||||||
"name": "gitlab-runner",
|
"name": "gitlab-runner",
|
||||||
|
@ -556,6 +607,7 @@ Example response:
|
||||||
"description": "Test 2",
|
"description": "Test 2",
|
||||||
"ip_address": "127.0.0.1",
|
"ip_address": "127.0.0.1",
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"paused": false,
|
||||||
"is_shared": false,
|
"is_shared": false,
|
||||||
"runner_type": "group_type",
|
"runner_type": "group_type",
|
||||||
"name": "gitlab-runner",
|
"name": "gitlab-runner",
|
||||||
|
@ -573,19 +625,20 @@ Register a new runner for the instance.
|
||||||
POST /runners
|
POST /runners
|
||||||
```
|
```
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
|--------------------|--------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------|--------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `token` | string | yes | [Registration token](#registration-and-authentication-tokens). |
|
| `token` | string | yes | [Registration token](#registration-and-authentication-tokens) |
|
||||||
| `description` | string | no | Runner's description |
|
| `description` | string | no | Runner's description |
|
||||||
| `info` | hash | no | Runner's metadata. You can include `name`, `version`, `revision`, `platform`, and `architecture`, but only `version` is displayed in the Admin area of the UI. |
|
| `info` | hash | no | Runner's metadata. You can include `name`, `version`, `revision`, `platform`, and `architecture`, but only `version` is displayed in the Admin area of the UI |
|
||||||
| `active` | boolean | no | Whether the runner is active |
|
| `active` | boolean | no | Deprecated: Use `:paused` instead. Whether the runner is allowed to receive jobs |
|
||||||
| `locked` | boolean | no | Whether the runner should be locked for current project |
|
| `paused` | boolean | no | Whether the runner should ignore new jobs |
|
||||||
| `run_untagged` | boolean | no | Whether the runner should handle untagged jobs |
|
| `locked` | boolean | no | Whether the runner should be locked for current project |
|
||||||
| `tag_list` | string array | no | List of runner's tags |
|
| `run_untagged` | boolean | no | Whether the runner should handle untagged jobs |
|
||||||
| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
|
| `tag_list` | string array | no | List of runner's tags |
|
||||||
| `maximum_timeout` | integer | no | Maximum timeout set when this runner handles the job |
|
| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
|
||||||
| `maintainer_note` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/350730), see `maintenance_note`. |
|
| `maximum_timeout` | integer | no | Maximum timeout set when this runner handles the job |
|
||||||
| `maintenance_note` | string | no | Free-form maintenance notes for the runner (255 characters) |
|
| `maintainer_note` | string | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/350730), see `maintenance_note` |
|
||||||
|
| `maintenance_note` | string | no | Free-form maintenance notes for the runner (255 characters) |
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --request POST "https://gitlab.example.com/api/v4/runners" \
|
curl --request POST "https://gitlab.example.com/api/v4/runners" \
|
||||||
|
|
|
@ -226,7 +226,7 @@ In milestone 15.0, we will remove the `pipelines` attribute from the API respons
|
||||||
|
|
||||||
**Planned removal milestone: 15.0 (2022-05-22)**
|
**Planned removal milestone: 15.0 (2022-05-22)**
|
||||||
|
|
||||||
### REST API Runner will not contain `paused`
|
### REST and GraphQL API Runner status will not return `paused`
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
This feature will be changed or removed in 15.0
|
This feature will be changed or removed in 15.0
|
||||||
|
@ -240,7 +240,7 @@ A runner's status will only relate to runner contact status, such as:
|
||||||
`online`, `offline`, or `not_connected`. Status `paused` or `active` will no longer appear.
|
`online`, `offline`, or `not_connected`. Status `paused` or `active` will no longer appear.
|
||||||
|
|
||||||
When checking if a runner is `paused`, API users are advised to check the boolean attribute
|
When checking if a runner is `paused`, API users are advised to check the boolean attribute
|
||||||
`active` to be `false` instead. When checking if a runner is `active`, check if `active` is `true`.
|
`paused` to be `true` instead. When checking if a runner is `active`, check if `paused` is `false`.
|
||||||
|
|
||||||
**Planned removal milestone: 15.0 (2022-05-22)**
|
**Planned removal milestone: 15.0 (2022-05-22)**
|
||||||
|
|
||||||
|
@ -717,6 +717,36 @@ The `merged_by` field in the [merge request API](https://docs.gitlab.com/ee/api/
|
||||||
|
|
||||||
## 14.8
|
## 14.8
|
||||||
|
|
||||||
|
### REST and GraphQL API Runner usage of `active` replaced by `paused`
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
This feature will be changed or removed in 15.0
|
||||||
|
as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes).
|
||||||
|
Before updating GitLab, review the details carefully to determine if you need to make any
|
||||||
|
changes to your code, settings, or workflow.
|
||||||
|
|
||||||
|
Occurrences of the `active` identifier in the GitLab Runner REST and GraphQL API endpoints will be
|
||||||
|
renamed to `paused` in GitLab 15.0, namely:
|
||||||
|
|
||||||
|
- GraphQL API:
|
||||||
|
- the `CiRunner` property;
|
||||||
|
- the `RunnerUpdateInput` input type for the `runnerUpdate` mutation;
|
||||||
|
- the `runners` and `Group.runners` queries.
|
||||||
|
- REST API:
|
||||||
|
- endpoints taking or returning `active` properties, such as:
|
||||||
|
- `GET /runners`
|
||||||
|
- `GET /runners/all`
|
||||||
|
- `GET /runners/:id` / `PUT /runners/:id`
|
||||||
|
- `PUT --form "active=false" /runners/:runner_id`
|
||||||
|
- `GET /projects/:id/runners` / `POST /projects/:id/runners`
|
||||||
|
- `GET /groups/:id/runners`
|
||||||
|
|
||||||
|
The 15.0 release of the GitLab Runner will start using the `paused` property when registering runners, and therefore
|
||||||
|
will only be compatible with GitLab 15.0 and later. Until 15.0, GitLab will accept the deprecated `active` flag from
|
||||||
|
existing runners.
|
||||||
|
|
||||||
|
**Planned removal milestone: 15.0 (2022-05-22)**
|
||||||
|
|
||||||
### Vulnerability Check
|
### Vulnerability Check
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
|
|
|
@ -18,21 +18,25 @@ module API
|
||||||
optional :maintainer_note, type: String, desc: %q(Deprecated: Use :maintenance_note instead. Runner's maintenance notes)
|
optional :maintainer_note, type: String, desc: %q(Deprecated: Use :maintenance_note instead. Runner's maintenance notes)
|
||||||
optional :maintenance_note, type: String, desc: %q(Runner's maintenance notes)
|
optional :maintenance_note, type: String, desc: %q(Runner's maintenance notes)
|
||||||
optional :info, type: Hash, desc: %q(Runner's metadata)
|
optional :info, type: Hash, desc: %q(Runner's metadata)
|
||||||
optional :active, type: Boolean, desc: 'Should Runner be active'
|
optional :active, type: Boolean, desc: 'Deprecated: Use `:paused` instead. Should runner be active'
|
||||||
optional :locked, type: Boolean, desc: 'Should Runner be locked for current project'
|
optional :paused, type: Boolean, desc: 'Whether the runner should ignore new jobs'
|
||||||
|
optional :locked, type: Boolean, desc: 'Whether the runner should be locked for current project'
|
||||||
optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
|
optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
|
||||||
desc: 'The access_level of the runner'
|
desc: 'The access_level of the runner; `not_protected` or `ref_protected`'
|
||||||
optional :run_untagged, type: Boolean, desc: 'Should Runner handle untagged jobs'
|
optional :run_untagged, type: Boolean, desc: 'Whether the runner should handle untagged jobs'
|
||||||
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: %q(List of Runner's tags)
|
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: %q(List of Runner's tags)
|
||||||
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
|
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this runner handles the job'
|
||||||
|
mutually_exclusive :maintainer_note, :maintainer_note
|
||||||
|
mutually_exclusive :active, :paused
|
||||||
end
|
end
|
||||||
post '/', feature_category: :runner do
|
post '/', feature_category: :runner do
|
||||||
attributes = attributes_for_keys(%i[description maintainer_note maintenance_note active locked run_untagged tag_list access_level maximum_timeout])
|
attributes = attributes_for_keys(%i[description maintainer_note maintenance_note active paused locked run_untagged tag_list access_level maximum_timeout])
|
||||||
.merge(get_runner_details_from_request)
|
.merge(get_runner_details_from_request)
|
||||||
|
|
||||||
# Pull in deprecated maintainer_note if that's the only note value available
|
# Pull in deprecated maintainer_note if that's the only note value available
|
||||||
deprecated_note = attributes.delete(:maintainer_note)
|
deprecated_note = attributes.delete(:maintainer_note)
|
||||||
attributes[:maintenance_note] ||= deprecated_note if deprecated_note
|
attributes[:maintenance_note] ||= deprecated_note if deprecated_note
|
||||||
|
attributes[:active] = !attributes.delete(:paused) if attributes.include?(:paused)
|
||||||
|
|
||||||
@runner = ::Ci::RegisterRunnerService.new.execute(params[:token], attributes)
|
@runner = ::Ci::RegisterRunnerService.new.execute(params[:token], attributes)
|
||||||
forbidden! unless @runner
|
forbidden! unless @runner
|
||||||
|
|
|
@ -77,18 +77,21 @@ module API
|
||||||
params do
|
params do
|
||||||
requires :id, type: Integer, desc: 'The ID of the runner'
|
requires :id, type: Integer, desc: 'The ID of the runner'
|
||||||
optional :description, type: String, desc: 'The description of the runner'
|
optional :description, type: String, desc: 'The description of the runner'
|
||||||
optional :active, type: Boolean, desc: 'The state of a runner'
|
optional :active, type: Boolean, desc: 'Deprecated: Use `:paused` instead. Flag indicating whether the runner is allowed to receive jobs'
|
||||||
|
optional :paused, type: Boolean, desc: 'Flag indicating whether the runner should ignore new jobs'
|
||||||
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of tags for a runner'
|
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of tags for a runner'
|
||||||
optional :run_untagged, type: Boolean, desc: 'Flag indicating the runner can execute untagged jobs'
|
optional :run_untagged, type: Boolean, desc: 'Flag indicating whether the runner can execute untagged jobs'
|
||||||
optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
|
optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
|
||||||
optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
|
optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
|
||||||
desc: 'The access_level of the runner'
|
desc: 'The access_level of the runner'
|
||||||
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
|
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
|
||||||
at_least_one_of :description, :active, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
|
at_least_one_of :description, :active, :paused, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
|
||||||
|
mutually_exclusive :active, :paused
|
||||||
end
|
end
|
||||||
put ':id' do
|
put ':id' do
|
||||||
runner = get_runner(params.delete(:id))
|
runner = get_runner(params.delete(:id))
|
||||||
authenticate_update_runner!(runner)
|
authenticate_update_runner!(runner)
|
||||||
|
params[:active] = !params.delete(:paused) if params.include?(:paused)
|
||||||
update_service = ::Ci::UpdateRunnerService.new(runner)
|
update_service = ::Ci::UpdateRunnerService.new(runner)
|
||||||
|
|
||||||
if update_service.update(declared_params(include_missing: false))
|
if update_service.update(declared_params(include_missing: false))
|
||||||
|
|
|
@ -7,7 +7,10 @@ module API
|
||||||
expose :id
|
expose :id
|
||||||
expose :description
|
expose :description
|
||||||
expose :ip_address
|
expose :ip_address
|
||||||
expose :active
|
expose :active # TODO Remove in %15.0 in favor of `paused` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/351109
|
||||||
|
expose :paused do |runner|
|
||||||
|
!runner.active
|
||||||
|
end
|
||||||
expose :instance_type?, as: :is_shared
|
expose :instance_type?, as: :is_shared
|
||||||
expose :runner_type
|
expose :runner_type
|
||||||
expose :name
|
expose :name
|
||||||
|
|
|
@ -83,6 +83,10 @@ ci_namespace_mirrors:
|
||||||
- table: namespaces
|
- table: namespaces
|
||||||
column: namespace_id
|
column: namespace_id
|
||||||
on_delete: async_delete
|
on_delete: async_delete
|
||||||
|
ci_sources_projects:
|
||||||
|
- table: projects
|
||||||
|
column: source_project_id
|
||||||
|
on_delete: async_delete
|
||||||
ci_build_report_results:
|
ci_build_report_results:
|
||||||
- table: projects
|
- table: projects
|
||||||
column: project_id
|
column: project_id
|
||||||
|
@ -167,6 +171,9 @@ ci_pipeline_schedules:
|
||||||
- table: users
|
- table: users
|
||||||
column: owner_id
|
column: owner_id
|
||||||
on_delete: async_nullify
|
on_delete: async_nullify
|
||||||
|
- table: projects
|
||||||
|
column: project_id
|
||||||
|
on_delete: async_delete
|
||||||
merge_trains:
|
merge_trains:
|
||||||
- table: ci_pipelines
|
- table: ci_pipelines
|
||||||
column: pipeline_id
|
column: pipeline_id
|
||||||
|
|
|
@ -18,7 +18,7 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.subdomain_regex
|
def self.subdomain_regex
|
||||||
%r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}.freeze
|
%r{\Ahttps://[a-z0-9-]+\.gitlab\.com\z}.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.dev_url
|
def self.dev_url
|
||||||
|
|
|
@ -20,6 +20,7 @@ module GoogleApi
|
||||||
"https://www.googleapis.com/auth/logging.write",
|
"https://www.googleapis.com/auth/logging.write",
|
||||||
"https://www.googleapis.com/auth/monitoring"
|
"https://www.googleapis.com/auth/monitoring"
|
||||||
].freeze
|
].freeze
|
||||||
|
ROLES_LIST = %w[roles/iam.serviceAccountUser roles/artifactregistry.admin roles/cloudbuild.builds.builder roles/run.admin roles/storage.admin roles/cloudsql.admin roles/browser].freeze
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def session_key_for_token
|
def session_key_for_token
|
||||||
|
@ -88,11 +89,8 @@ module GoogleApi
|
||||||
def list_projects
|
def list_projects
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
service = Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService.new
|
response = cloud_resource_manager_service.fetch_all(items: :projects) do |token|
|
||||||
service.authorization = access_token
|
cloud_resource_manager_service.list_projects
|
||||||
|
|
||||||
response = service.fetch_all(items: :projects) do |token|
|
|
||||||
service.list_projects
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Google API results are paged by default, so we need to iterate through
|
# Google API results are paged by default, so we need to iterate through
|
||||||
|
@ -130,6 +128,11 @@ module GoogleApi
|
||||||
service.create_service_account_key(name, request_body)
|
service.create_service_account_key(name, request_body)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def grant_service_account_roles(gcp_project_id, email)
|
||||||
|
body = policy_request_body(gcp_project_id, email)
|
||||||
|
cloud_resource_manager_service.set_project_iam_policy(gcp_project_id, body)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def make_cluster_options(cluster_name, cluster_size, machine_type, legacy_abac, enable_addons)
|
def make_cluster_options(cluster_name, cluster_size, machine_type, legacy_abac, enable_addons)
|
||||||
|
@ -173,6 +176,23 @@ module GoogleApi
|
||||||
options.header = { 'User-Agent': "GitLab/#{Gitlab::VERSION.match('(\d+\.\d+)').captures.first} (GPN:GitLab;)" }
|
options.header = { 'User-Agent': "GitLab/#{Gitlab::VERSION.match('(\d+\.\d+)').captures.first} (GPN:GitLab;)" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def policy_request_body(gcp_project_id, email)
|
||||||
|
policy = cloud_resource_manager_service.get_project_iam_policy(gcp_project_id)
|
||||||
|
policy.bindings = policy.bindings + additional_policy_bindings("serviceAccount:#{email}")
|
||||||
|
|
||||||
|
Google::Apis::CloudresourcemanagerV1::SetIamPolicyRequest.new(policy: policy)
|
||||||
|
end
|
||||||
|
|
||||||
|
def additional_policy_bindings(member)
|
||||||
|
ROLES_LIST.map do |role|
|
||||||
|
Google::Apis::CloudresourcemanagerV1::Binding.new(role: role, members: [member])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def cloud_resource_manager_service
|
||||||
|
@gpc_service ||= Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService.new.tap { |s| s. authorization = access_token }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -127,7 +127,7 @@ RSpec.describe 'Merge request > User creates image diff notes', :js do
|
||||||
visit diffs_project_merge_request_path(project, merge_request, view: view)
|
visit diffs_project_merge_request_path(project, merge_request, view: view)
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
|
|
||||||
expect(page.all('.diff-file span.label-lfs', visible: :all)).not_to be_empty
|
expect(page.all('[data-testid="label-lfs"]', visible: :all)).not_to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'creates image diff note'
|
it_behaves_like 'creates image diff note'
|
||||||
|
|
|
@ -82,7 +82,7 @@ describe('DiffFileHeader component', () => {
|
||||||
const findExpandButton = () => wrapper.find({ ref: 'expandDiffToFullFileButton' });
|
const findExpandButton = () => wrapper.find({ ref: 'expandDiffToFullFileButton' });
|
||||||
const findFileActions = () => wrapper.find('.file-actions');
|
const findFileActions = () => wrapper.find('.file-actions');
|
||||||
const findModeChangedLine = () => wrapper.find({ ref: 'fileMode' });
|
const findModeChangedLine = () => wrapper.find({ ref: 'fileMode' });
|
||||||
const findLfsLabel = () => wrapper.find('.label-lfs');
|
const findLfsLabel = () => wrapper.find('[data-testid="label-lfs"]');
|
||||||
const findToggleDiscussionsButton = () => wrapper.find({ ref: 'toggleDiscussionsButton' });
|
const findToggleDiscussionsButton = () => wrapper.find({ ref: 'toggleDiscussionsButton' });
|
||||||
const findExternalLink = () => wrapper.find({ ref: 'externalLink' });
|
const findExternalLink = () => wrapper.find({ ref: 'externalLink' });
|
||||||
const findReplacedFileButton = () => wrapper.find({ ref: 'replacedFileButton' });
|
const findReplacedFileButton = () => wrapper.find({ ref: 'replacedFileButton' });
|
||||||
|
|
|
@ -19,12 +19,10 @@ RSpec.describe 'cross-database foreign keys' do
|
||||||
ci_pending_builds.namespace_id
|
ci_pending_builds.namespace_id
|
||||||
ci_pending_builds.project_id
|
ci_pending_builds.project_id
|
||||||
ci_pipeline_schedules.owner_id
|
ci_pipeline_schedules.owner_id
|
||||||
ci_pipeline_schedules.project_id
|
|
||||||
ci_pipelines.project_id
|
ci_pipelines.project_id
|
||||||
ci_resource_groups.project_id
|
ci_resource_groups.project_id
|
||||||
ci_runner_namespaces.namespace_id
|
ci_runner_namespaces.namespace_id
|
||||||
ci_running_builds.project_id
|
ci_running_builds.project_id
|
||||||
ci_sources_projects.source_project_id
|
|
||||||
ci_stages.project_id
|
ci_stages.project_id
|
||||||
ci_unit_tests.project_id
|
ci_unit_tests.project_id
|
||||||
).freeze
|
).freeze
|
||||||
|
|
|
@ -99,6 +99,13 @@ RSpec.describe Gitlab do
|
||||||
expect(described_class.com?).to eq true
|
expect(described_class.com?).to eq true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'is true when on other gitlab subdomain with hyphen' do
|
||||||
|
url_with_subdomain = Gitlab::Saas.com_url.gsub('https://', 'https://test-example.')
|
||||||
|
stub_config_setting(url: url_with_subdomain)
|
||||||
|
|
||||||
|
expect(described_class.com?).to eq true
|
||||||
|
end
|
||||||
|
|
||||||
it 'is false when not on GitLab.com' do
|
it 'is false when not on GitLab.com' do
|
||||||
stub_config_setting(url: 'http://example.com')
|
stub_config_setting(url: 'http://example.com')
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
|
||||||
before do
|
before do
|
||||||
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
|
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
|
||||||
.to receive(:get_zone_cluster).with(any_args, options: user_agent_options)
|
.to receive(:get_zone_cluster).with(any_args, options: user_agent_options)
|
||||||
.and_return(gke_cluster)
|
.and_return(gke_cluster)
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to eq(gke_cluster) }
|
it { is_expected.to eq(gke_cluster) }
|
||||||
|
@ -122,7 +122,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
|
||||||
before do
|
before do
|
||||||
allow_any_instance_of(Google::Apis::ContainerV1beta1::ContainerService)
|
allow_any_instance_of(Google::Apis::ContainerV1beta1::ContainerService)
|
||||||
.to receive(:create_cluster).with(any_args)
|
.to receive(:create_cluster).with(any_args)
|
||||||
.and_return(operation)
|
.and_return(operation)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets corresponded parameters' do
|
it 'sets corresponded parameters' do
|
||||||
|
@ -172,7 +172,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
|
||||||
before do
|
before do
|
||||||
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
|
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
|
||||||
.to receive(:get_zone_operation).with(any_args, options: user_agent_options)
|
.to receive(:get_zone_operation).with(any_args, options: user_agent_options)
|
||||||
.and_return(operation)
|
.and_return(operation)
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to eq(operation) }
|
it { is_expected.to eq(operation) }
|
||||||
|
@ -244,7 +244,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
|
||||||
|
|
||||||
let(:operation) { double('Service Account Key') }
|
let(:operation) { double('Service Account Key') }
|
||||||
|
|
||||||
it 'class Google Api IamService#create_service_account_key' do
|
it 'calls Google Api IamService#create_service_account_key' do
|
||||||
expect_any_instance_of(Google::Apis::IamV1::IamService)
|
expect_any_instance_of(Google::Apis::IamV1::IamService)
|
||||||
.to receive(:create_service_account_key)
|
.to receive(:create_service_account_key)
|
||||||
.with(any_args)
|
.with(any_args)
|
||||||
|
@ -252,4 +252,48 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
|
||||||
is_expected.to eq(operation)
|
is_expected.to eq(operation)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'grant_service_account_roles' do
|
||||||
|
subject { client.grant_service_account_roles(spy, spy) }
|
||||||
|
|
||||||
|
it 'calls Google Api CloudResourceManager#set_iam_policy' do
|
||||||
|
mock_gcp_id = 'mock-gcp-id'
|
||||||
|
mock_email = 'mock@email.com'
|
||||||
|
mock_policy = Struct.new(:bindings).new([])
|
||||||
|
mock_body = []
|
||||||
|
|
||||||
|
expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
|
||||||
|
.with({ 'role': 'roles/iam.serviceAccountUser', 'members': ["serviceAccount:#{mock_email}"] })
|
||||||
|
|
||||||
|
expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
|
||||||
|
.with({ 'role': 'roles/artifactregistry.admin', 'members': ["serviceAccount:#{mock_email}"] })
|
||||||
|
|
||||||
|
expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
|
||||||
|
.with({ 'role': 'roles/cloudbuild.builds.builder', 'members': ["serviceAccount:#{mock_email}"] })
|
||||||
|
|
||||||
|
expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
|
||||||
|
.with({ 'role': 'roles/run.admin', 'members': ["serviceAccount:#{mock_email}"] })
|
||||||
|
|
||||||
|
expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
|
||||||
|
.with({ 'role': 'roles/storage.admin', 'members': ["serviceAccount:#{mock_email}"] })
|
||||||
|
|
||||||
|
expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
|
||||||
|
.with({ 'role': 'roles/cloudsql.admin', 'members': ["serviceAccount:#{mock_email}"] })
|
||||||
|
|
||||||
|
expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
|
||||||
|
.with({ 'role': 'roles/browser', 'members': ["serviceAccount:#{mock_email}"] })
|
||||||
|
|
||||||
|
expect(Google::Apis::CloudresourcemanagerV1::SetIamPolicyRequest).to receive(:new).and_return([])
|
||||||
|
|
||||||
|
expect_next_instance_of(Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService) do |instance|
|
||||||
|
expect(instance).to receive(:get_project_iam_policy)
|
||||||
|
.with(mock_gcp_id)
|
||||||
|
.and_return(mock_policy)
|
||||||
|
expect(instance).to receive(:set_project_iam_policy)
|
||||||
|
.with(mock_gcp_id, mock_body)
|
||||||
|
end
|
||||||
|
|
||||||
|
client.grant_service_account_roles(mock_gcp_id, mock_email)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
65
spec/migrations/20220124130028_dedup_runner_projects_spec.rb
Normal file
65
spec/migrations/20220124130028_dedup_runner_projects_spec.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require Rails.root.join('db', 'post_migrate', '20220124130028_dedup_runner_projects.rb')
|
||||||
|
|
||||||
|
RSpec.describe DedupRunnerProjects, :migration, schema: 20220120085655 do
|
||||||
|
let(:namespaces) { table(:namespaces) }
|
||||||
|
let(:projects) { table(:projects) }
|
||||||
|
let(:runners) { table(:ci_runners) }
|
||||||
|
let(:runner_projects) { table(:ci_runner_projects) }
|
||||||
|
|
||||||
|
let!(:namespace) { namespaces.create!(name: 'foo', path: 'foo') }
|
||||||
|
let!(:project) { projects.create!(namespace_id: namespace.id) }
|
||||||
|
let!(:project_2) { projects.create!(namespace_id: namespace.id) }
|
||||||
|
let!(:runner) { runners.create!(runner_type: 'project_type') }
|
||||||
|
let!(:runner_2) { runners.create!(runner_type: 'project_type') }
|
||||||
|
let!(:runner_3) { runners.create!(runner_type: 'project_type') }
|
||||||
|
|
||||||
|
let!(:duplicated_runner_project_1) { runner_projects.create!(runner_id: runner.id, project_id: project.id) }
|
||||||
|
let!(:duplicated_runner_project_2) { runner_projects.create!(runner_id: runner.id, project_id: project.id) }
|
||||||
|
let!(:duplicated_runner_project_3) { runner_projects.create!(runner_id: runner_2.id, project_id: project_2.id) }
|
||||||
|
let!(:duplicated_runner_project_4) { runner_projects.create!(runner_id: runner_2.id, project_id: project_2.id) }
|
||||||
|
|
||||||
|
let!(:non_duplicated_runner_project) { runner_projects.create!(runner_id: runner_3.id, project_id: project.id) }
|
||||||
|
|
||||||
|
it 'deduplicates ci_runner_projects table' do
|
||||||
|
expect { migrate! }.to change { runner_projects.count }.from(5).to(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'merges `duplicated_runner_project_1` with `duplicated_runner_project_2`', :aggregate_failures do
|
||||||
|
migrate!
|
||||||
|
|
||||||
|
expect(runner_projects.where(id: duplicated_runner_project_1.id)).not_to(exist)
|
||||||
|
|
||||||
|
merged_runner_projects = runner_projects.find_by(id: duplicated_runner_project_2.id)
|
||||||
|
|
||||||
|
expect(merged_runner_projects).to be_present
|
||||||
|
expect(merged_runner_projects.created_at).to be_like_time(duplicated_runner_project_1.created_at)
|
||||||
|
expect(merged_runner_projects.created_at).to be_like_time(duplicated_runner_project_2.created_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'merges `duplicated_runner_project_3` with `duplicated_runner_project_4`', :aggregate_failures do
|
||||||
|
migrate!
|
||||||
|
|
||||||
|
expect(runner_projects.where(id: duplicated_runner_project_3.id)).not_to(exist)
|
||||||
|
|
||||||
|
merged_runner_projects = runner_projects.find_by(id: duplicated_runner_project_4.id)
|
||||||
|
|
||||||
|
expect(merged_runner_projects).to be_present
|
||||||
|
expect(merged_runner_projects.created_at).to be_like_time(duplicated_runner_project_3.created_at)
|
||||||
|
expect(merged_runner_projects.created_at).to be_like_time(duplicated_runner_project_4.created_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not change non duplicated records' do
|
||||||
|
expect { migrate! }.not_to change { non_duplicated_runner_project.reload.attributes }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does nothing when there are no runner projects' do
|
||||||
|
runner_projects.delete_all
|
||||||
|
|
||||||
|
migrate!
|
||||||
|
|
||||||
|
expect(runner_projects.count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
|
@ -227,4 +227,11 @@ RSpec.describe Ci::PipelineSchedule do
|
||||||
it { is_expected.to eq(144) }
|
it { is_expected.to eq(144) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'loose foreign key on ci_pipeline_schedules.project_id' do
|
||||||
|
it_behaves_like 'cleanup by a loose foreign key' do
|
||||||
|
let!(:parent) { create(:project) }
|
||||||
|
let!(:model) { create(:ci_pipeline_schedule, project: parent) }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
||||||
run_untagged: false,
|
run_untagged: false,
|
||||||
tag_list: 'tag1, tag2',
|
tag_list: 'tag1, tag2',
|
||||||
locked: true,
|
locked: true,
|
||||||
active: true,
|
paused: false,
|
||||||
access_level: 'ref_protected',
|
access_level: 'ref_protected',
|
||||||
maximum_timeout: 9000
|
maximum_timeout: 9000
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
||||||
maximum_timeout: 9000
|
maximum_timeout: 9000
|
||||||
}.stringify_keys
|
}.stringify_keys
|
||||||
|
|
||||||
allow(service).to receive(:execute)
|
expect(service).to receive(:execute)
|
||||||
.once
|
.once
|
||||||
.with('valid token', a_hash_including(expected_params))
|
.with('valid token', a_hash_including(expected_params))
|
||||||
.and_return(new_runner)
|
.and_return(new_runner)
|
||||||
|
@ -108,6 +108,32 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when deprecated active parameter is provided' do
|
||||||
|
def request
|
||||||
|
post api('/runners'), params: {
|
||||||
|
token: 'valid token',
|
||||||
|
active: false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
let_it_be(:new_runner) { create(:ci_runner) }
|
||||||
|
|
||||||
|
it 'uses active value in registration' do
|
||||||
|
expect_next_instance_of(::Ci::RegisterRunnerService) do |service|
|
||||||
|
expected_params = { active: false }.stringify_keys
|
||||||
|
|
||||||
|
expect(service).to receive(:execute)
|
||||||
|
.once
|
||||||
|
.with('valid token', a_hash_including(expected_params))
|
||||||
|
.and_return(new_runner)
|
||||||
|
end
|
||||||
|
|
||||||
|
request
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'calling actual register service' do
|
context 'calling actual register service' do
|
||||||
include StubGitlabCalls
|
include StubGitlabCalls
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ RSpec.describe API::Ci::Runners do
|
||||||
get api('/runners?tag_list=tag1,tag2', user)
|
get api('/runners?tag_list=tag1,tag2', user)
|
||||||
|
|
||||||
expect(json_response).to match_array [
|
expect(json_response).to match_array [
|
||||||
a_hash_including('description' => 'Runner tagged with tag1 and tag2')
|
a_hash_including('description' => 'Runner tagged with tag1 and tag2', 'active' => true, 'paused' => false)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -137,7 +137,7 @@ RSpec.describe API::Ci::Runners do
|
||||||
get api('/runners/all', admin)
|
get api('/runners/all', admin)
|
||||||
|
|
||||||
expect(json_response).to match_array [
|
expect(json_response).to match_array [
|
||||||
a_hash_including('description' => 'Project runner', 'is_shared' => false, 'runner_type' => 'project_type'),
|
a_hash_including('description' => 'Project runner', 'is_shared' => false, 'active' => true, 'paused' => false, 'runner_type' => 'project_type'),
|
||||||
a_hash_including('description' => 'Two projects runner', 'is_shared' => false, 'runner_type' => 'project_type'),
|
a_hash_including('description' => 'Two projects runner', 'is_shared' => false, 'runner_type' => 'project_type'),
|
||||||
a_hash_including('description' => 'Group runner A', 'is_shared' => false, 'runner_type' => 'group_type'),
|
a_hash_including('description' => 'Group runner A', 'is_shared' => false, 'runner_type' => 'group_type'),
|
||||||
a_hash_including('description' => 'Group runner B', 'is_shared' => false, 'runner_type' => 'group_type'),
|
a_hash_including('description' => 'Group runner B', 'is_shared' => false, 'runner_type' => 'group_type'),
|
||||||
|
@ -255,6 +255,8 @@ RSpec.describe API::Ci::Runners do
|
||||||
expect(json_response['description']).to eq(shared_runner.description)
|
expect(json_response['description']).to eq(shared_runner.description)
|
||||||
expect(json_response['maximum_timeout']).to be_nil
|
expect(json_response['maximum_timeout']).to be_nil
|
||||||
expect(json_response['status']).to eq("not_connected")
|
expect(json_response['status']).to eq("not_connected")
|
||||||
|
expect(json_response['active']).to eq(true)
|
||||||
|
expect(json_response['paused']).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -359,6 +361,14 @@ RSpec.describe API::Ci::Runners do
|
||||||
expect(shared_runner.reload.active).to eq(!active)
|
expect(shared_runner.reload.active).to eq(!active)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'runner paused state' do
|
||||||
|
active = shared_runner.active
|
||||||
|
update_runner(shared_runner.id, admin, paused: active)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(shared_runner.reload.active).to eq(!active)
|
||||||
|
end
|
||||||
|
|
||||||
it 'runner tag list' do
|
it 'runner tag list' do
|
||||||
update_runner(shared_runner.id, admin, tag_list: ['ruby2.1', 'pgsql', 'mysql'])
|
update_runner(shared_runner.id, admin, tag_list: ['ruby2.1', 'pgsql', 'mysql'])
|
||||||
|
|
||||||
|
@ -908,9 +918,9 @@ RSpec.describe API::Ci::Runners do
|
||||||
get api("/projects/#{project.id}/runners", user)
|
get api("/projects/#{project.id}/runners", user)
|
||||||
|
|
||||||
expect(json_response).to match_array [
|
expect(json_response).to match_array [
|
||||||
a_hash_including('description' => 'Project runner'),
|
a_hash_including('description' => 'Project runner', 'active' => true, 'paused' => false),
|
||||||
a_hash_including('description' => 'Two projects runner'),
|
a_hash_including('description' => 'Two projects runner', 'active' => true, 'paused' => false),
|
||||||
a_hash_including('description' => 'Shared runner')
|
a_hash_including('description' => 'Shared runner', 'active' => true, 'paused' => false)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -986,7 +996,7 @@ RSpec.describe API::Ci::Runners do
|
||||||
get api("/groups/#{group.id}/runners", user)
|
get api("/groups/#{group.id}/runners", user)
|
||||||
|
|
||||||
expect(json_response).to match_array([
|
expect(json_response).to match_array([
|
||||||
a_hash_including('description' => 'Group runner A')
|
a_hash_including('description' => 'Group runner A', 'active' => true, 'paused' => false)
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
# Mock Types
|
|
||||||
MockGoogleOAuth2Credentials = Struct.new(:app_id, :app_secret)
|
|
||||||
MockServiceAccount = Struct.new(:project_id, :unique_id)
|
|
||||||
|
|
||||||
RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
|
RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
|
||||||
let_it_be(:project) { create(:project, :public) }
|
let_it_be(:project) { create(:project, :public) }
|
||||||
|
|
||||||
|
@ -86,10 +82,12 @@ RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
|
||||||
context 'and user has successfully completed the google oauth2 flow' do
|
context 'and user has successfully completed the google oauth2 flow' do
|
||||||
before do
|
before do
|
||||||
allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
|
allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
|
||||||
|
mock_service_account = Struct.new(:project_id, :unique_id, :email).new(123, 456, 'em@ai.l')
|
||||||
allow(client).to receive(:validate_token).and_return(true)
|
allow(client).to receive(:validate_token).and_return(true)
|
||||||
allow(client).to receive(:list_projects).and_return([{}, {}, {}])
|
allow(client).to receive(:list_projects).and_return([{}, {}, {}])
|
||||||
allow(client).to receive(:create_service_account).and_return(MockServiceAccount.new(123, 456))
|
allow(client).to receive(:create_service_account).and_return(mock_service_account)
|
||||||
allow(client).to receive(:create_service_account_key).and_return({})
|
allow(client).to receive(:create_service_account_key).and_return({})
|
||||||
|
allow(client).to receive(:grant_service_account_roles)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -147,7 +145,8 @@ RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
|
||||||
|
|
||||||
context 'but gitlab instance is not configured for google oauth2' do
|
context 'but gitlab instance is not configured for google oauth2' do
|
||||||
before do
|
before do
|
||||||
unconfigured_google_oauth2 = MockGoogleOAuth2Credentials.new('', '')
|
unconfigured_google_oauth2 = Struct.new(:app_id, :app_secret)
|
||||||
|
.new('', '')
|
||||||
allow(Gitlab::Auth::OAuth::Provider).to receive(:config_for)
|
allow(Gitlab::Auth::OAuth::Provider).to receive(:config_for)
|
||||||
.with('google_oauth2')
|
.with('google_oauth2')
|
||||||
.and_return(unconfigured_google_oauth2)
|
.and_return(unconfigured_google_oauth2)
|
||||||
|
|
|
@ -32,5 +32,22 @@ RSpec.describe Ci::PipelineScheduleService do
|
||||||
expect { subject }.not_to raise_error
|
expect { subject }.not_to raise_error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the project is missing' do
|
||||||
|
before do
|
||||||
|
project.delete
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not raise an exception' do
|
||||||
|
expect { subject }.not_to raise_error
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not run RunPipelineScheduleWorker' do
|
||||||
|
expect(RunPipelineScheduleWorker)
|
||||||
|
.not_to receive(:perform_async).with(schedule.id, schedule.owner.id)
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,6 +23,20 @@ RSpec.describe Ci::UpdateRunnerService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with paused param' do
|
||||||
|
let(:params) { { paused: true } }
|
||||||
|
|
||||||
|
it 'updates the runner and ticking the queue' do
|
||||||
|
expect(runner.active).to be_truthy
|
||||||
|
expect(update).to be_truthy
|
||||||
|
|
||||||
|
runner.reload
|
||||||
|
|
||||||
|
expect(runner).to have_received(:tick_runner_queue)
|
||||||
|
expect(runner.active).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with cost factor params' do
|
context 'with cost factor params' do
|
||||||
let(:params) { { public_projects_minutes_cost_factor: 1.1, private_projects_minutes_cost_factor: 2.2 }}
|
let(:params) { { public_projects_minutes_cost_factor: 1.1, private_projects_minutes_cost_factor: 2.2 }}
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,26 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
# Mock Types
|
|
||||||
MockGoogleOAuth2Credentials = Struct.new(:app_id, :app_secret)
|
|
||||||
MockServiceAccount = Struct.new(:project_id, :unique_id)
|
|
||||||
|
|
||||||
RSpec.describe GoogleCloud::CreateServiceAccountsService do
|
RSpec.describe GoogleCloud::CreateServiceAccountsService do
|
||||||
describe '#execute' do
|
describe '#execute' do
|
||||||
before do
|
before do
|
||||||
|
mock_google_oauth2_creds = Struct.new(:app_id, :app_secret)
|
||||||
|
.new('mock-app-id', 'mock-app-secret')
|
||||||
allow(Gitlab::Auth::OAuth::Provider).to receive(:config_for)
|
allow(Gitlab::Auth::OAuth::Provider).to receive(:config_for)
|
||||||
.with('google_oauth2')
|
.with('google_oauth2')
|
||||||
.and_return(MockGoogleOAuth2Credentials.new('mock-app-id', 'mock-app-secret'))
|
.and_return(mock_google_oauth2_creds)
|
||||||
|
|
||||||
allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
|
allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
|
||||||
|
mock_service_account = Struct.new(:project_id, :unique_id, :email)
|
||||||
|
.new('mock-project-id', 'mock-unique-id', 'mock-email')
|
||||||
allow(client).to receive(:create_service_account)
|
allow(client).to receive(:create_service_account)
|
||||||
.and_return(MockServiceAccount.new('mock-project-id', 'mock-unique-id'))
|
.and_return(mock_service_account)
|
||||||
|
|
||||||
allow(client).to receive(:create_service_account_key)
|
allow(client).to receive(:create_service_account_key)
|
||||||
.and_return('mock-key')
|
.and_return('mock-key')
|
||||||
|
|
||||||
|
allow(client)
|
||||||
|
.to receive(:grant_service_account_roles)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,16 @@ RSpec.describe Ci::DeleteObjectsWorker do
|
||||||
let(:worker) { described_class.new }
|
let(:worker) { described_class.new }
|
||||||
|
|
||||||
it { expect(described_class.idempotent?).to be_truthy }
|
it { expect(described_class.idempotent?).to be_truthy }
|
||||||
|
it { is_expected.to respond_to(:max_running_jobs) }
|
||||||
|
it { is_expected.to respond_to(:remaining_work_count) }
|
||||||
|
it { is_expected.to respond_to(:perform_work) }
|
||||||
|
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
it 'executes a service' do
|
it 'executes a service' do
|
||||||
allow(worker).to receive(:max_running_jobs).and_return(25)
|
|
||||||
|
|
||||||
expect_next_instance_of(Ci::DeleteObjectsService) do |instance|
|
expect_next_instance_of(Ci::DeleteObjectsService) do |instance|
|
||||||
expect(instance).to receive(:execute)
|
expect(instance).to receive(:execute)
|
||||||
expect(instance).to receive(:remaining_batches_count)
|
expect(instance).to receive(:remaining_batches_count)
|
||||||
.with(max_batch_count: 25)
|
.with(max_batch_count: 20)
|
||||||
.once
|
.once
|
||||||
.and_call_original
|
.and_call_original
|
||||||
end
|
end
|
||||||
|
@ -22,30 +23,4 @@ RSpec.describe Ci::DeleteObjectsWorker do
|
||||||
worker.perform
|
worker.perform
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#max_running_jobs' do
|
|
||||||
using RSpec::Parameterized::TableSyntax
|
|
||||||
|
|
||||||
before do
|
|
||||||
stub_feature_flags(
|
|
||||||
ci_delete_objects_medium_concurrency: medium,
|
|
||||||
ci_delete_objects_high_concurrency: high
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject(:max_running_jobs) { worker.max_running_jobs }
|
|
||||||
|
|
||||||
where(:medium, :high, :expected) do
|
|
||||||
false | false | 2
|
|
||||||
true | false | 20
|
|
||||||
true | true | 20
|
|
||||||
false | true | 50
|
|
||||||
end
|
|
||||||
|
|
||||||
with_them do
|
|
||||||
it 'sets up concurrency depending on the feature flag' do
|
|
||||||
expect(max_running_jobs).to eq(expected)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -103,4 +103,14 @@ RSpec.describe PipelineScheduleWorker do
|
||||||
expect { subject }.not_to raise_error
|
expect { subject }.not_to raise_error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the project is missing' do
|
||||||
|
before do
|
||||||
|
project.delete
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not raise an exception' do
|
||||||
|
expect { subject }.not_to raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,12 +10,25 @@ RSpec.describe RunPipelineScheduleWorker do
|
||||||
|
|
||||||
let(:worker) { described_class.new }
|
let(:worker) { described_class.new }
|
||||||
|
|
||||||
context 'when a project not found' do
|
context 'when a schedule not found' do
|
||||||
it 'does not call the Service' do
|
it 'does not call the Service' do
|
||||||
expect(Ci::CreatePipelineService).not_to receive(:new)
|
expect(Ci::CreatePipelineService).not_to receive(:new)
|
||||||
expect(worker).not_to receive(:run_pipeline_schedule)
|
expect(worker).not_to receive(:run_pipeline_schedule)
|
||||||
|
|
||||||
worker.perform(100000, user.id)
|
worker.perform(non_existing_record_id, user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a schedule project is missing' do
|
||||||
|
before do
|
||||||
|
project.delete
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not call the Service' do
|
||||||
|
expect(Ci::CreatePipelineService).not_to receive(:new)
|
||||||
|
expect(worker).not_to receive(:run_pipeline_schedule)
|
||||||
|
|
||||||
|
worker.perform(pipeline_schedule.id, user.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -24,7 +37,7 @@ RSpec.describe RunPipelineScheduleWorker do
|
||||||
expect(Ci::CreatePipelineService).not_to receive(:new)
|
expect(Ci::CreatePipelineService).not_to receive(:new)
|
||||||
expect(worker).not_to receive(:run_pipeline_schedule)
|
expect(worker).not_to receive(:run_pipeline_schedule)
|
||||||
|
|
||||||
worker.perform(pipeline_schedule.id, 10000)
|
worker.perform(pipeline_schedule.id, non_existing_record_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue