Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-01-28 15:17:26 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-01-28 15:17:26 +0300
commitd6ce16a4070112512d792f7ab66fdca4764cbf2c (patch)
treea85a3c45b5fb1bfd66badf100198cbc0036227e9
parentfa4a0663e8c12b7b80e9e7edec99e6ca5a04a253 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue6
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss2
-rw-r--r--app/assets/stylesheets/framework/files.scss5
-rw-r--r--app/assets/stylesheets/startup/startup-dark.scss2
-rw-r--r--app/assets/stylesheets/startup/startup-general.scss2
-rw-r--r--app/graphql/mutations/ci/runner/update.rb7
-rw-r--r--app/services/ci/pipeline_schedule_service.rb2
-rw-r--r--app/services/ci/update_runner_service.rb2
-rw-r--r--app/services/google_cloud/create_service_accounts_service.rb5
-rw-r--r--app/views/projects/blob/_header_content.html.haml2
-rw-r--r--app/workers/ci/delete_objects_worker.rb8
-rw-r--r--app/workers/pipeline_schedule_worker.rb2
-rw-r--r--app/workers/run_pipeline_schedule_worker.rb2
-rw-r--r--config/feature_flags/development/project_import_schedule_worker_job_tracker.yml (renamed from config/feature_flags/development/ci_delete_objects_medium_concurrency.yml)10
-rw-r--r--config/feature_flags/development/update_all_mirrors_job_tracker.yml (renamed from config/feature_flags/development/ci_delete_objects_high_concurrency.yml)10
-rw-r--r--data/deprecations/14-5-runner-api-status-does-contain-paused.yml4
-rw-r--r--data/deprecations/14-8-runner-api-active-field-replaced-with-paused-breaking-change.yml33
-rw-r--r--db/post_migrate/20220124130028_dedup_runner_projects.rb71
-rw-r--r--db/post_migrate/20220126202654_remove_projects_ci_sources_projects_source_project_id_fk.rb19
-rw-r--r--db/post_migrate/20220126203421_remove_projects_ci_pipeline_schedules_project_id_fk.rb19
-rw-r--r--db/schema_migrations/202201241300281
-rw-r--r--db/schema_migrations/202201262026541
-rw-r--r--db/schema_migrations/202201262034211
-rw-r--r--db/structure.sql10
-rw-r--r--doc/api/graphql/reference/index.md3
-rw-r--r--doc/api/runners.md103
-rw-r--r--doc/update/deprecations.md34
-rw-r--r--lib/api/ci/runner.rb16
-rw-r--r--lib/api/ci/runners.rb9
-rw-r--r--lib/api/entities/ci/runner.rb5
-rw-r--r--lib/gitlab/database/gitlab_loose_foreign_keys.yml7
-rw-r--r--lib/gitlab/saas.rb2
-rw-r--r--lib/google_api/cloud_platform/client.rb30
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb2
-rw-r--r--spec/frontend/diffs/components/diff_file_header_spec.js2
-rw-r--r--spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb2
-rw-r--r--spec/lib/gitlab_spec.rb7
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb52
-rw-r--r--spec/migrations/20220124130028_dedup_runner_projects_spec.rb65
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb7
-rw-r--r--spec/requests/api/ci/runner/runners_post_spec.rb30
-rw-r--r--spec/requests/api/ci/runners_spec.rb22
-rw-r--r--spec/requests/projects/google_cloud/service_accounts_controller_spec.rb11
-rw-r--r--spec/services/ci/pipeline_schedule_service_spec.rb17
-rw-r--r--spec/services/ci/update_runner_service_spec.rb14
-rw-r--r--spec/services/google_cloud/create_service_accounts_service_spec.rb16
-rw-r--r--spec/workers/ci/delete_objects_worker_spec.rb33
-rw-r--r--spec/workers/pipeline_schedule_worker_spec.rb10
-rw-r--r--spec/workers/run_pipeline_schedule_worker_spec.rb19
50 files changed, 603 insertions, 143 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 580ba589977..1b322111644 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-b7f0c0462a8f689c8ee9e654f0875157b238158b
+69baea2ed2c593ed570b614f1c311d8ee80a1c33
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 238f07ac22c..0e8506cd896 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -3,6 +3,7 @@ import {
GlTooltipDirective,
GlSafeHtmlDirective,
GlIcon,
+ GlBadge,
GlButton,
GlButtonGroup,
GlDropdown,
@@ -34,6 +35,7 @@ export default {
GlIcon,
FileIcon,
DiffStats,
+ GlBadge,
GlButton,
GlButtonGroup,
GlDropdown,
@@ -349,7 +351,9 @@ export default {
{{ diffFile.a_mode }} → {{ diffFile.b_mode }}
</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
diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index 2a3ed29258a..3f3e29f8cc8 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -267,6 +267,8 @@
.nav-item-name {
flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
> a,
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 9209a0c2173..9387500e66f 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -411,11 +411,6 @@ span.idiff {
margin-right: 1.5em;
}
-.label-lfs {
- color: $common-gray-light;
- border: 1px solid $common-gray-light;
-}
-
.preview-container {
overflow: auto;
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index 9acf650af43..574a1652548 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -1095,6 +1095,8 @@ input {
}
.nav-sidebar li .nav-item-name {
flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.nav-sidebar li > a,
.nav-sidebar li > .fly-out-top-item-container {
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index 645fa42f832..7eced0f3b07 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -1076,6 +1076,8 @@ input {
}
.nav-sidebar li .nav-item-name {
flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.nav-sidebar li > a,
.nav-sidebar li > .fly-out-top-item-container {
diff --git a/app/graphql/mutations/ci/runner/update.rb b/app/graphql/mutations/ci/runner/update.rb
index e37ab1081f9..e6123b4283a 100644
--- a/app/graphql/mutations/ci/runner/update.rb
+++ b/app/graphql/mutations/ci/runner/update.rb
@@ -28,7 +28,12 @@ module Mutations
argument :active, GraphQL::Types::Boolean,
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,
description: 'Indicates the runner is locked.'
diff --git a/app/services/ci/pipeline_schedule_service.rb b/app/services/ci/pipeline_schedule_service.rb
index 596c3b80bda..536eaa56f9b 100644
--- a/app/services/ci/pipeline_schedule_service.rb
+++ b/app/services/ci/pipeline_schedule_service.rb
@@ -3,6 +3,8 @@
module Ci
class PipelineScheduleService < BaseService
def execute(schedule)
+ return unless project.persisted?
+
# Ensure `next_run_at` is set properly before creating a pipeline.
# Otherwise, multiple pipelines could be created in a short interval.
schedule.schedule_next_run!
diff --git a/app/services/ci/update_runner_service.rb b/app/services/ci/update_runner_service.rb
index e4117a51fe6..4a17e25c0cc 100644
--- a/app/services/ci/update_runner_service.rb
+++ b/app/services/ci/update_runner_service.rb
@@ -9,6 +9,8 @@ module Ci
end
def update(params)
+ params[:active] = !params.delete(:paused) if params.include?(:paused)
+
runner.update(params).tap do |updated|
runner.tick_runner_queue if updated
end
diff --git a/app/services/google_cloud/create_service_accounts_service.rb b/app/services/google_cloud/create_service_accounts_service.rb
index fa025e8f672..e360b3a8e4e 100644
--- a/app/services/google_cloud/create_service_accounts_service.rb
+++ b/app/services/google_cloud/create_service_accounts_service.rb
@@ -5,6 +5,7 @@ module GoogleCloud
def execute
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)
+ google_api_client.grant_service_account_roles(gcp_project_id, service_account.email)
service_accounts_service.add_for_project(
environment_name,
@@ -35,7 +36,7 @@ module GoogleCloud
end
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
def service_accounts_service
@@ -50,7 +51,7 @@ module GoogleCloud
"GitLab generated service account for project '#{project.name}' and environment '#{environment_name}'"
end
- # Overriden in EE
+ # Overridden in EE
def environment_protected?
false
end
diff --git a/app/views/projects/blob/_header_content.html.haml b/app/views/projects/blob/_header_content.html.haml
index 95a5d63e07f..f5e32e7f589 100644
--- a/app/views/projects/blob/_header_content.html.haml
+++ b/app/views/projects/blob/_header_content.html.haml
@@ -14,4 +14,4 @@
= number_to_human_size(blob.raw_size)
- if blob.stored_externally? && blob.external_storage == :lfs
- %span.badge.label-lfs.gl-mr-2 LFS
+ = gl_badge_tag(_('LFS'), variant: :neutral)
diff --git a/app/workers/ci/delete_objects_worker.rb b/app/workers/ci/delete_objects_worker.rb
index cbcad3e8838..32c57750076 100644
--- a/app/workers/ci/delete_objects_worker.rb
+++ b/app/workers/ci/delete_objects_worker.rb
@@ -22,13 +22,7 @@ module Ci
end
def max_running_jobs
- if ::Feature.enabled?(:ci_delete_objects_medium_concurrency)
- 20
- elsif ::Feature.enabled?(:ci_delete_objects_high_concurrency)
- 50
- else
- 2
- end
+ 20
end
private
diff --git a/app/workers/pipeline_schedule_worker.rb b/app/workers/pipeline_schedule_worker.rb
index ebda30f57d8..5a53d53ccf9 100644
--- a/app/workers/pipeline_schedule_worker.rb
+++ b/app/workers/pipeline_schedule_worker.rb
@@ -13,6 +13,8 @@ class PipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
def perform
Ci::PipelineSchedule.runnable_schedules.preloaded.find_in_batches do |schedules|
schedules.each do |schedule|
+ next unless schedule.project
+
with_context(project: schedule.project, user: schedule.owner) do
Ci::PipelineScheduleService.new(schedule.project, schedule.owner).execute(schedule)
end
diff --git a/app/workers/run_pipeline_schedule_worker.rb b/app/workers/run_pipeline_schedule_worker.rb
index f08d8231e43..35e3e633c70 100644
--- a/app/workers/run_pipeline_schedule_worker.rb
+++ b/app/workers/run_pipeline_schedule_worker.rb
@@ -15,7 +15,7 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
schedule = Ci::PipelineSchedule.find_by_id(schedule_id)
user = User.find_by_id(user_id)
- return unless schedule && user
+ return unless schedule && schedule.project && user
run_pipeline_schedule(schedule, user)
end
diff --git a/config/feature_flags/development/ci_delete_objects_medium_concurrency.yml b/config/feature_flags/development/project_import_schedule_worker_job_tracker.yml
index 55fc2d9fc94..5dae4ddc60c 100644
--- a/config/feature_flags/development/ci_delete_objects_medium_concurrency.yml
+++ b/config/feature_flags/development/project_import_schedule_worker_job_tracker.yml
@@ -1,8 +1,8 @@
---
-name: ci_delete_objects_medium_concurrency
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39464
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/247103
-milestone: '13.5'
+name: project_import_schedule_worker_job_tracker
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79097
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351408
+milestone: '14.8'
type: development
-group: group::pipeline execution
+group: group::scalability
default_enabled: false
diff --git a/config/feature_flags/development/ci_delete_objects_high_concurrency.yml b/config/feature_flags/development/update_all_mirrors_job_tracker.yml
index a14861beb59..507f32550c3 100644
--- a/config/feature_flags/development/ci_delete_objects_high_concurrency.yml
+++ b/config/feature_flags/development/update_all_mirrors_job_tracker.yml
@@ -1,8 +1,8 @@
---
-name: ci_delete_objects_high_concurrency
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39464
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/247103
-milestone: '13.5'
+name: update_all_mirrors_job_tracker
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79097
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351420
+milestone: '14.8'
type: development
-group: group::pipeline execution
+group: group::scalability
default_enabled: false
diff --git a/data/deprecations/14-5-runner-api-status-does-contain-paused.yml b/data/deprecations/14-5-runner-api-status-does-contain-paused.yml
index dd2e6e7a6fb..c3fbe553706 100644
--- a/data/deprecations/14-5-runner-api-status-does-contain-paused.yml
+++ b/data/deprecations/14-5-runner-api-status-does-contain-paused.yml
@@ -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_date: "2021-11-22"
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.
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
tiers: [Core, Premium, Ultimate]
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344648
diff --git a/data/deprecations/14-8-runner-api-active-field-replaced-with-paused-breaking-change.yml b/data/deprecations/14-8-runner-api-active-field-replaced-with-paused-breaking-change.yml
new file mode 100644
index 00000000000..791c44d19eb
--- /dev/null
+++ b/data/deprecations/14-8-runner-api-active-field-replaced-with-paused-breaking-change.yml
@@ -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
diff --git a/db/post_migrate/20220124130028_dedup_runner_projects.rb b/db/post_migrate/20220124130028_dedup_runner_projects.rb
new file mode 100644
index 00000000000..1a4c895f1c4
--- /dev/null
+++ b/db/post_migrate/20220124130028_dedup_runner_projects.rb
@@ -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
diff --git a/db/post_migrate/20220126202654_remove_projects_ci_sources_projects_source_project_id_fk.rb b/db/post_migrate/20220126202654_remove_projects_ci_sources_projects_source_project_id_fk.rb
new file mode 100644
index 00000000000..a69cd43b921
--- /dev/null
+++ b/db/post_migrate/20220126202654_remove_projects_ci_sources_projects_source_project_id_fk.rb
@@ -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
diff --git a/db/post_migrate/20220126203421_remove_projects_ci_pipeline_schedules_project_id_fk.rb b/db/post_migrate/20220126203421_remove_projects_ci_pipeline_schedules_project_id_fk.rb
new file mode 100644
index 00000000000..870127ab168
--- /dev/null
+++ b/db/post_migrate/20220126203421_remove_projects_ci_pipeline_schedules_project_id_fk.rb
@@ -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
diff --git a/db/schema_migrations/20220124130028 b/db/schema_migrations/20220124130028
new file mode 100644
index 00000000000..5ee8e463a55
--- /dev/null
+++ b/db/schema_migrations/20220124130028
@@ -0,0 +1 @@
+7f2b3e70e33273d75f68bd1fa33103f24a4e4cfc3f2e5777dfd258b5a2e7bf4e \ No newline at end of file
diff --git a/db/schema_migrations/20220126202654 b/db/schema_migrations/20220126202654
new file mode 100644
index 00000000000..341e2c6b8bf
--- /dev/null
+++ b/db/schema_migrations/20220126202654
@@ -0,0 +1 @@
+6067e4e22e49548496454b48171f8168f7d5bf626fedab4351e2a37a3f85731b \ No newline at end of file
diff --git a/db/schema_migrations/20220126203421 b/db/schema_migrations/20220126203421
new file mode 100644
index 00000000000..32469cd40ee
--- /dev/null
+++ b/db/schema_migrations/20220126203421
@@ -0,0 +1 @@
+07f837ddde21e36d1ca6a471dd96350d3020bd30204fca0e093983810c94e54d \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index b98c24f6c4a..deb58fe6708 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -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_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_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_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 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
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
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
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
ADD CONSTRAINT fk_rails_64bfc19bc5 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 11ac51e9726..12ea1e13b57 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -4158,12 +4158,13 @@ Input type: `RunnerUpdateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <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="mutationrunnerupdatedescription"></a>`description` | [`String`](#string) | Description of the runner. |
| <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="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="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. |
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 163bcdad588..aa676746450 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -54,12 +54,17 @@ GET /runners?tag_list=tag1,tag2
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:
```json
[
{
"active": true,
+ "paused": false,
"description": "test-1-20150125",
"id": 6,
"ip_address": "127.0.0.1",
@@ -71,6 +76,7 @@ Example response:
},
{
"active": true,
+ "paused": false,
"description": "test-2-20150125",
"id": 8,
"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"
```
+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:
```json
[
{
"active": true,
+ "paused": false,
"description": "shared-runner-1",
"id": 1,
"ip_address": "127.0.0.1",
@@ -124,6 +135,7 @@ Example response:
},
{
"active": true,
+ "paused": false,
"description": "shared-runner-2",
"id": 3,
"ip_address": "127.0.0.1",
@@ -135,6 +147,7 @@ Example response:
},
{
"active": true,
+ "paused": false,
"description": "test-1-20150125",
"id": 6,
"ip_address": "127.0.0.1",
@@ -146,6 +159,7 @@ Example response:
},
{
"active": true,
+ "paused": false,
"description": "test-2-20150125",
"id": 8,
"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).
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:
```json
{
"active": true,
+ "paused": false,
"architecture": null,
"description": "test-1-20150125",
"id": 6,
@@ -229,16 +248,17 @@ Update details of a runner.
PUT /runners/:id
```
-| Attribute | Type | Required | Description |
-|---------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID 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` |
-| `tag_list` | array | no | The list of tags for a runner; put array of tags, that should be finally assigned to a runner |
-| `run_untagged`| boolean | no | Flag indicating the runner can execute untagged jobs |
-| `locked` | boolean | no | Flag indicating the runner is locked |
-| `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 |
+| Attribute | Type | Required | Description |
+|-------------------|---------|----------|--------------------------------------------------------------------------------------------------|
+| `id` | integer | yes | The ID of a runner |
+| `description` | string | no | The description of a runner |
+| `active` | boolean | no | Deprecated: Use `:paused` instead. Flag indicating whether the runner is allowed to receive jobs |
+| `paused` | boolean | no | Flag indicating whether the runner should ignore new jobs |
+| `tag_list` | array | no | The list of tags for a runner; put array of tags, that should be finally assigned to a runner |
+| `run_untagged` | boolean | no | Flag indicating the runner can execute untagged jobs |
+| `locked` | boolean | no | Flag indicating the runner is locked |
+| `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
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.
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:
```json
@@ -292,7 +316,12 @@ Example response:
Pause a specific runner.
```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 |
@@ -301,9 +330,19 @@ PUT --form "active=false" /runners/:runner_id
```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>" \
--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
> [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"
```
+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:
```json
[
{
"active": true,
+ "paused": false,
"description": "test-2-20150125",
"id": 8,
"ip_address": "127.0.0.1",
@@ -437,6 +481,7 @@ Example response:
},
{
"active": true,
+ "paused": false,
"description": "development_runner",
"id": 5,
"ip_address": "127.0.0.1",
@@ -444,7 +489,7 @@ Example response:
"runner_type": "instance_type",
"name": null,
"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"
```
+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:
```json
@@ -534,6 +583,7 @@ Example response:
"description": "Shared",
"ip_address": "127.0.0.1",
"active": true,
+ "paused": false,
"is_shared": true,
"runner_type": "instance_type",
"name": "gitlab-runner",
@@ -545,6 +595,7 @@ Example response:
"description": "Test",
"ip_address": "127.0.0.1",
"active": true,
+ "paused": false,
"is_shared": true,
"runner_type": "instance_type",
"name": "gitlab-runner",
@@ -556,6 +607,7 @@ Example response:
"description": "Test 2",
"ip_address": "127.0.0.1",
"active": true,
+ "paused": false,
"is_shared": false,
"runner_type": "group_type",
"name": "gitlab-runner",
@@ -573,19 +625,20 @@ Register a new runner for the instance.
POST /runners
```
-| Attribute | Type | Required | Description |
-|--------------------|--------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `token` | string | yes | [Registration token](#registration-and-authentication-tokens). |
-| `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. |
-| `active` | boolean | no | Whether the runner is active |
-| `locked` | boolean | no | Whether the runner should be locked for current project |
-| `run_untagged` | boolean | no | Whether the runner should handle untagged jobs |
-| `tag_list` | string array | no | List of runner's tags |
-| `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 |
-| `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) |
+| Attribute | Type | Required | Description |
+|--------------------|--------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `token` | string | yes | [Registration token](#registration-and-authentication-tokens) |
+| `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 |
+| `active` | boolean | no | Deprecated: Use `:paused` instead. Whether the runner is allowed to receive jobs |
+| `paused` | boolean | no | Whether the runner should ignore new jobs |
+| `locked` | boolean | no | Whether the runner should be locked for current project |
+| `run_untagged` | boolean | no | Whether the runner should handle untagged jobs |
+| `tag_list` | string array | no | List of runner's tags |
+| `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 |
+| `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
curl --request POST "https://gitlab.example.com/api/v4/runners" \
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index 8327e42dabf..9dbb2d2e8f2 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -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)**
-### REST API Runner will not contain `paused`
+### REST and GraphQL API Runner status will not return `paused`
WARNING:
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.
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)**
@@ -717,6 +717,36 @@ The `merged_by` field in the [merge request API](https://docs.gitlab.com/ee/api/
## 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
WARNING:
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index aa75a6f2410..153738a8edc 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -18,21 +18,25 @@ module API
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 :info, type: Hash, desc: %q(Runner's metadata)
- optional :active, type: Boolean, desc: 'Should Runner be active'
- optional :locked, type: Boolean, desc: 'Should Runner be locked for current project'
+ optional :active, type: Boolean, desc: 'Deprecated: Use `:paused` instead. Should runner be active'
+ 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,
- desc: 'The access_level of the runner'
- optional :run_untagged, type: Boolean, desc: 'Should Runner handle untagged jobs'
+ desc: 'The access_level of the runner; `not_protected` or `ref_protected`'
+ 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 :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
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)
# Pull in deprecated maintainer_note if that's the only note value available
deprecated_note = attributes.delete(:maintainer_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)
forbidden! unless @runner
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index f21782a698f..9b3b9ca3f48 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -77,18 +77,21 @@ module API
params do
requires :id, type: Integer, desc: 'The ID 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 :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 :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
desc: 'The access_level of the runner'
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
put ':id' do
runner = get_runner(params.delete(:id))
authenticate_update_runner!(runner)
+ params[:active] = !params.delete(:paused) if params.include?(:paused)
update_service = ::Ci::UpdateRunnerService.new(runner)
if update_service.update(declared_params(include_missing: false))
diff --git a/lib/api/entities/ci/runner.rb b/lib/api/entities/ci/runner.rb
index c17ff513479..a6944b8c925 100644
--- a/lib/api/entities/ci/runner.rb
+++ b/lib/api/entities/ci/runner.rb
@@ -7,7 +7,10 @@ module API
expose :id
expose :description
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 :runner_type
expose :name
diff --git a/lib/gitlab/database/gitlab_loose_foreign_keys.yml b/lib/gitlab/database/gitlab_loose_foreign_keys.yml
index 4a796b6c265..c80163290ec 100644
--- a/lib/gitlab/database/gitlab_loose_foreign_keys.yml
+++ b/lib/gitlab/database/gitlab_loose_foreign_keys.yml
@@ -83,6 +83,10 @@ ci_namespace_mirrors:
- table: namespaces
column: namespace_id
on_delete: async_delete
+ci_sources_projects:
+ - table: projects
+ column: source_project_id
+ on_delete: async_delete
ci_build_report_results:
- table: projects
column: project_id
@@ -167,6 +171,9 @@ ci_pipeline_schedules:
- table: users
column: owner_id
on_delete: async_nullify
+ - table: projects
+ column: project_id
+ on_delete: async_delete
merge_trains:
- table: ci_pipelines
column: pipeline_id
diff --git a/lib/gitlab/saas.rb b/lib/gitlab/saas.rb
index 577e33fd700..0a4f2ba64a8 100644
--- a/lib/gitlab/saas.rb
+++ b/lib/gitlab/saas.rb
@@ -18,7 +18,7 @@ module Gitlab
end
def self.subdomain_regex
- %r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}.freeze
+ %r{\Ahttps://[a-z0-9-]+\.gitlab\.com\z}.freeze
end
def self.dev_url
diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb
index 9bd2309d2b7..defcb33c5b6 100644
--- a/lib/google_api/cloud_platform/client.rb
+++ b/lib/google_api/cloud_platform/client.rb
@@ -20,6 +20,7 @@ module GoogleApi
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring"
].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
def session_key_for_token
@@ -88,11 +89,8 @@ module GoogleApi
def list_projects
result = []
- service = Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService.new
- service.authorization = access_token
-
- response = service.fetch_all(items: :projects) do |token|
- service.list_projects
+ response = cloud_resource_manager_service.fetch_all(items: :projects) do |token|
+ cloud_resource_manager_service.list_projects
end
# 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)
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
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;)" }
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
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index cc0d7a279dd..47529518ba4 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -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)
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
it_behaves_like 'creates image diff note'
diff --git a/spec/frontend/diffs/components/diff_file_header_spec.js b/spec/frontend/diffs/components/diff_file_header_spec.js
index 320fb395c3b..f22bd312a6d 100644
--- a/spec/frontend/diffs/components/diff_file_header_spec.js
+++ b/spec/frontend/diffs/components/diff_file_header_spec.js
@@ -82,7 +82,7 @@ describe('DiffFileHeader component', () => {
const findExpandButton = () => wrapper.find({ ref: 'expandDiffToFullFileButton' });
const findFileActions = () => wrapper.find('.file-actions');
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 findExternalLink = () => wrapper.find({ ref: 'externalLink' });
const findReplacedFileButton = () => wrapper.find({ ref: 'replacedFileButton' });
diff --git a/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb b/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb
index c5ebd762a79..84dd2e98c18 100644
--- a/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb
+++ b/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb
@@ -19,12 +19,10 @@ RSpec.describe 'cross-database foreign keys' do
ci_pending_builds.namespace_id
ci_pending_builds.project_id
ci_pipeline_schedules.owner_id
- ci_pipeline_schedules.project_id
ci_pipelines.project_id
ci_resource_groups.project_id
ci_runner_namespaces.namespace_id
ci_running_builds.project_id
- ci_sources_projects.source_project_id
ci_stages.project_id
ci_unit_tests.project_id
).freeze
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index 49ba4debe31..57a4bdc9bb5 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -99,6 +99,13 @@ RSpec.describe Gitlab do
expect(described_class.com?).to eq true
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
stub_config_setting(url: 'http://example.com')
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index 3284c9cd0d1..ab45a648555 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -60,7 +60,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
before do
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
.to receive(:get_zone_cluster).with(any_args, options: user_agent_options)
- .and_return(gke_cluster)
+ .and_return(gke_cluster)
end
it { is_expected.to eq(gke_cluster) }
@@ -122,7 +122,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
before do
allow_any_instance_of(Google::Apis::ContainerV1beta1::ContainerService)
.to receive(:create_cluster).with(any_args)
- .and_return(operation)
+ .and_return(operation)
end
it 'sets corresponded parameters' do
@@ -172,7 +172,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
before do
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
.to receive(:get_zone_operation).with(any_args, options: user_agent_options)
- .and_return(operation)
+ .and_return(operation)
end
it { is_expected.to eq(operation) }
@@ -244,7 +244,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
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)
.to receive(:create_service_account_key)
.with(any_args)
@@ -252,4 +252,48 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
is_expected.to eq(operation)
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
diff --git a/spec/migrations/20220124130028_dedup_runner_projects_spec.rb b/spec/migrations/20220124130028_dedup_runner_projects_spec.rb
new file mode 100644
index 00000000000..2698af6f6f5
--- /dev/null
+++ b/spec/migrations/20220124130028_dedup_runner_projects_spec.rb
@@ -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
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index 0f1cb721e95..0f4f148775e 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -227,4 +227,11 @@ RSpec.describe Ci::PipelineSchedule do
it { is_expected.to eq(144) }
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
diff --git a/spec/requests/api/ci/runner/runners_post_spec.rb b/spec/requests/api/ci/runner/runners_post_spec.rb
index 6409c08c278..27917933c68 100644
--- a/spec/requests/api/ci/runner/runners_post_spec.rb
+++ b/spec/requests/api/ci/runner/runners_post_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
run_untagged: false,
tag_list: 'tag1, tag2',
locked: true,
- active: true,
+ paused: false,
access_level: 'ref_protected',
maximum_timeout: 9000
}
@@ -55,7 +55,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
maximum_timeout: 9000
}.stringify_keys
- allow(service).to receive(:execute)
+ expect(service).to receive(:execute)
.once
.with('valid token', a_hash_including(expected_params))
.and_return(new_runner)
@@ -108,6 +108,32 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
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
include StubGitlabCalls
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index 305c0bd9df0..7cf436969db 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -109,7 +109,7 @@ RSpec.describe API::Ci::Runners do
get api('/runners?tag_list=tag1,tag2', user)
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
@@ -137,7 +137,7 @@ RSpec.describe API::Ci::Runners do
get api('/runners/all', admin)
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' => 'Group runner A', '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['maximum_timeout']).to be_nil
expect(json_response['status']).to eq("not_connected")
+ expect(json_response['active']).to eq(true)
+ expect(json_response['paused']).to eq(false)
end
end
@@ -359,6 +361,14 @@ RSpec.describe API::Ci::Runners do
expect(shared_runner.reload.active).to eq(!active)
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
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)
expect(json_response).to match_array [
- a_hash_including('description' => 'Project runner'),
- a_hash_including('description' => 'Two projects runner'),
- a_hash_including('description' => 'Shared runner')
+ a_hash_including('description' => 'Project runner', 'active' => true, 'paused' => false),
+ a_hash_including('description' => 'Two projects runner', 'active' => true, 'paused' => false),
+ a_hash_including('description' => 'Shared runner', 'active' => true, 'paused' => false)
]
end
@@ -986,7 +996,7 @@ RSpec.describe API::Ci::Runners do
get api("/groups/#{group.id}/runners", user)
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
diff --git a/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb b/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb
index 6b4d1c490e2..0f243a6a7a9 100644
--- a/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb
@@ -2,10 +2,6 @@
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
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
before do
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(: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(:grant_service_account_roles)
end
end
@@ -147,7 +145,8 @@ RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
context 'but gitlab instance is not configured for google oauth2' 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)
.with('google_oauth2')
.and_return(unconfigured_google_oauth2)
diff --git a/spec/services/ci/pipeline_schedule_service_spec.rb b/spec/services/ci/pipeline_schedule_service_spec.rb
index 65bbd13c5e7..b8e4fb19f5d 100644
--- a/spec/services/ci/pipeline_schedule_service_spec.rb
+++ b/spec/services/ci/pipeline_schedule_service_spec.rb
@@ -32,5 +32,22 @@ RSpec.describe Ci::PipelineScheduleService do
expect { subject }.not_to raise_error
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
diff --git a/spec/services/ci/update_runner_service_spec.rb b/spec/services/ci/update_runner_service_spec.rb
index 1c875b2f54a..eee80bfef47 100644
--- a/spec/services/ci/update_runner_service_spec.rb
+++ b/spec/services/ci/update_runner_service_spec.rb
@@ -23,6 +23,20 @@ RSpec.describe Ci::UpdateRunnerService do
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
let(:params) { { public_projects_minutes_cost_factor: 1.1, private_projects_minutes_cost_factor: 2.2 }}
diff --git a/spec/services/google_cloud/create_service_accounts_service_spec.rb b/spec/services/google_cloud/create_service_accounts_service_spec.rb
index 190e1a8098c..53d21df713a 100644
--- a/spec/services/google_cloud/create_service_accounts_service_spec.rb
+++ b/spec/services/google_cloud/create_service_accounts_service_spec.rb
@@ -2,22 +2,26 @@
require 'spec_helper'
-# Mock Types
-MockGoogleOAuth2Credentials = Struct.new(:app_id, :app_secret)
-MockServiceAccount = Struct.new(:project_id, :unique_id)
-
RSpec.describe GoogleCloud::CreateServiceAccountsService do
describe '#execute' 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)
.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|
+ 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)
- .and_return(MockServiceAccount.new('mock-project-id', 'mock-unique-id'))
+ .and_return(mock_service_account)
+
allow(client).to receive(:create_service_account_key)
.and_return('mock-key')
+
+ allow(client)
+ .to receive(:grant_service_account_roles)
end
end
diff --git a/spec/workers/ci/delete_objects_worker_spec.rb b/spec/workers/ci/delete_objects_worker_spec.rb
index 52d90d7667a..3d985dffdc5 100644
--- a/spec/workers/ci/delete_objects_worker_spec.rb
+++ b/spec/workers/ci/delete_objects_worker_spec.rb
@@ -6,15 +6,16 @@ RSpec.describe Ci::DeleteObjectsWorker do
let(:worker) { described_class.new }
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
it 'executes a service' do
- allow(worker).to receive(:max_running_jobs).and_return(25)
-
expect_next_instance_of(Ci::DeleteObjectsService) do |instance|
expect(instance).to receive(:execute)
expect(instance).to receive(:remaining_batches_count)
- .with(max_batch_count: 25)
+ .with(max_batch_count: 20)
.once
.and_call_original
end
@@ -22,30 +23,4 @@ RSpec.describe Ci::DeleteObjectsWorker do
worker.perform
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
diff --git a/spec/workers/pipeline_schedule_worker_spec.rb b/spec/workers/pipeline_schedule_worker_spec.rb
index f59d8ad4615..4a7db0eca56 100644
--- a/spec/workers/pipeline_schedule_worker_spec.rb
+++ b/spec/workers/pipeline_schedule_worker_spec.rb
@@ -103,4 +103,14 @@ RSpec.describe PipelineScheduleWorker do
expect { subject }.not_to raise_error
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
diff --git a/spec/workers/run_pipeline_schedule_worker_spec.rb b/spec/workers/run_pipeline_schedule_worker_spec.rb
index bb11d1dbb58..846b4455bf9 100644
--- a/spec/workers/run_pipeline_schedule_worker_spec.rb
+++ b/spec/workers/run_pipeline_schedule_worker_spec.rb
@@ -10,12 +10,25 @@ RSpec.describe RunPipelineScheduleWorker do
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
expect(Ci::CreatePipelineService).not_to receive(:new)
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
@@ -24,7 +37,7 @@ RSpec.describe RunPipelineScheduleWorker do
expect(Ci::CreatePipelineService).not_to receive(:new)
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