Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-03-07 09:13:09 +00:00
parent 0ead22f9db
commit 75c2755b05
32 changed files with 617 additions and 89 deletions

View file

@ -1,6 +1,6 @@
<script>
import { GlFormGroup, GlFormCheckbox } from '@gitlab/ui';
import { mapGetters } from 'vuex';
import { mapGetters, mapState } from 'vuex';
export default {
name: 'ActiveCheckbox',
@ -15,6 +15,10 @@ export default {
},
computed: {
...mapGetters(['isInheriting', 'propsSource']),
...mapState(['customState']),
disabled() {
return this.isInheriting || this.customState.activateDisabled;
},
},
mounted() {
this.activated = this.propsSource.initialActivated;
@ -34,7 +38,7 @@ export default {
<gl-form-checkbox
v-model="activated"
class="gl-display-block"
:disabled="isInheriting"
:disabled="disabled"
@change="onChange"
>
{{ __('Active') }}

View file

@ -39,6 +39,7 @@ function parseDatasetToProps(data) {
const {
showActive,
activated,
activateDisabled,
editable,
canTest,
commitEvents,
@ -54,6 +55,7 @@ function parseDatasetToProps(data) {
return {
initialActivated: activated,
showActive,
activateDisabled,
type,
cancelPath,
editable,

View file

@ -104,7 +104,8 @@ module IntegrationsHelper
form_data = {
id: integration.id,
show_active: integration.show_active_box?.to_s,
activated: (integration.active || integration.new_record?).to_s,
activated: (integration.active || (integration.new_record? && integration.activate_disabled_reason.nil?)).to_s,
activate_disabled: integration.activate_disabled_reason.present?.to_s,
type: integration.to_param,
merge_request_events: integration.merge_requests_events.to_s,
commit_events: integration.commit_events.to_s,

View file

@ -8,7 +8,6 @@ class Deployment < ApplicationRecord
include Importable
include Gitlab::Utils::StrongMemoize
include FastDestroyAll
include FromUnion
StatusUpdateError = Class.new(StandardError)
StatusSyncError = Class.new(StandardError)

View file

@ -360,6 +360,10 @@ class Integration < ApplicationRecord
true
end
def activate_disabled_reason
nil
end
def category
read_attribute(:category).to_sym
end

View file

@ -132,8 +132,18 @@ module Integrations
# implement inside child
end
def activate_disabled_reason
{ trackers: other_external_issue_trackers } if other_external_issue_trackers.any?
end
private
def other_external_issue_trackers
return [] unless project_level?
@other_external_issue_trackers ||= project.integrations.external_issue_trackers.where.not(id: id)
end
def enabled_in_gitlab_config
Gitlab.config.issues_tracker &&
Gitlab.config.issues_tracker.values.any? &&
@ -148,7 +158,7 @@ module Integrations
return if instance?
return if project.blank?
if project.integrations.external_issue_trackers.where.not(id: id).any?
if other_external_issue_trackers.any?
errors.add(:base, _('Another issue tracker is already in use. Only one issue tracker service can be active at a time'))
end
end

View file

@ -1154,7 +1154,6 @@ class MergeRequest < ApplicationRecord
def mergeable_state?(skip_ci_check: false, skip_discussions_check: false)
return false unless open?
return false if work_in_progress?
return false if broken?
if Feature.enabled?(:improved_mergeability_checks, self.project, default_enabled: :yaml)
additional_checks = MergeRequests::Mergeability::RunChecksService.new(
@ -1166,6 +1165,7 @@ class MergeRequest < ApplicationRecord
)
additional_checks.execute.all?(&:success?)
else
return false if broken?
return false unless skip_discussions_check || mergeable_discussions_state?
return false unless skip_ci_check || mergeable_ci_state?

View file

@ -21,11 +21,13 @@ module Preloaders
def load_deployment_association(association_name, association_attributes)
return unless environments.present?
union_arg = environments.inject([]) do |result, environment|
result << environment.association(association_name).scope
end
union_sql = Deployment.from_union(union_arg).to_sql
# Not using Gitlab::SQL::Union as `order_by` in the SQL constructed is ignored.
# See:
# 1) https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/sql/union.rb#L7
# 2) https://gitlab.com/gitlab-org/gitlab/-/issues/353966#note_860928647
union_sql = environments.map do |environment|
"(#{environment.association(association_name).scope.to_sql})"
end.join(' UNION ')
deployments = Deployment
.from("(#{union_sql}) #{::Deployment.table_name}")

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
module MergeRequests
module Mergeability
class CheckBrokenStatusService < CheckBaseService
def execute
if merge_request.broken?
failure
else
success
end
end
def skip?
false
end
def cacheable?
false
end
end
end
end

View file

@ -7,6 +7,7 @@ module MergeRequests
# We want to have the cheapest checks first in the list,
# that way we can fail fast before running the more expensive ones
CHECKS = [
CheckBrokenStatusService,
CheckDiscussionsStatusService,
CheckCiStatusService
].freeze

View file

@ -1,6 +1,17 @@
- if lookup_context.template_exists?('top', "projects/services/#{integration.to_param}", true)
= render "projects/services/#{integration.to_param}/top", integration: integration
- if integration.activate_disabled_reason.present? && integration.activate_disabled_reason[:trackers].any?
-# When using integration.activate_disabled_reason[:trackers], it's potentially insecure to use the raw records
-# when passed directly to the frontend. Only use specific fields that are needed for render.
-# For example, we can get the link to each tracker with scoped_edit_integration_path(tracker, tracker.project)
= render 'shared/global_alert',
title: s_('ExternalIssueIntegration|Another issue tracker is already in use'),
variant: :warning,
dismissible: false do
.gl-alert-body
= s_('ExternalIssueIntegration|Only one issue tracker integration can be active at a time. Please disable the active tracker first and try again.')
%h3.page-title
= integration.title
- if integration.operating?

View file

@ -0,0 +1,6 @@
- name: "Move `custom_hooks_dir` setting from GitLab Shell to Gitaly" # The name of the feature to be deprecated
announcement_milestone: "14.9" # The milestone when this feature was first announced as deprecated.
announcement_date: "2021-10-22"
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
body: | # Do not modify this line, instead modify the lines below.
The [`custom_hooks_dir`](https://docs.gitlab.com/ee/administration/server_hooks.html#create-a-global-server-hook-for-all-repositories) setting is now configured in Gitaly, and will be removed from GitLab Shell in GitLab 15.0.

View file

@ -32,7 +32,7 @@ If you are using GDK, you can follow the following steps:
1. On the GDK root directory, run:
```shell
gdk config set gitlab.rails.multiple_databases true
gdk config set gitlab.rails.databases.ci.enabled true
```
1. Open your `gdk.yml`, and confirm that it has the following lines:

View file

@ -438,3 +438,61 @@ old method:
If you want to add easy Geo replication of a resource you're working
on, check out our [self-service framework](geo/framework.md).
## Geo development workflow
### GET:Geo pipeline
As part of the [package-and-qa](testing_guide/end_to_end/index.md#using-the-package-and-qa-job) pipeline, there is an option to manually trigger a job named `GET:Geo`. This
pipeline uses [GET](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) to spin up a
[1k](../administration/reference_architectures/1k_users.md) Geo installation,
and run the [`gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa) Geo scenario against the instance.
When working on Geo features, it is a good idea to ensure the `qa-geo` job passes in a triggered `GET:Geo pipeline`.
The pipelines that control the provisioning and teardown of the instance are included in The GitLab Environment Toolkit Configs
[Geo subproject](https://gitlab.com/gitlab-org/quality/gitlab-environment-toolkit-configs/Geo).
When adding new functionality, consider adding new tests to verify the behavior. For steps,
see the [QA documentation](https://gitlab.com/gitlab-org/gitlab/-/tree/master/qa#writing-tests).
#### Architecture
The pipeline involves the interaction of multiple different projects:
- [GitLab](https://gitlab.com/gitlab-org/gitlab) - The [package-and-qa job](testing_guide/end_to_end/index.md#using-the-package-and-qa-job) is launched from merge requests in this project.
- [`omnibus-gitlab`](https://gitlab.com/gitlab-org/omnibus-gitlab) - Builds relevant artifacts containing the changes from the triggering merge request pipeline.
- [GET-Configs/Geo](https://gitlab.com/gitlab-org/quality/gitlab-environment-toolkit-configs/Geo) - Coordinates the lifecycle of a short-lived Geo installation that can be evaluated.
- [GET](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) - Contains the necessary logic for creating and destroying Geo installations. Used by `GET-Configs/Geo`.
- [`gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa) - Tool for running automated tests against a GitLab instance.
```mermaid
flowchart TD;
GET:Geo-->getcg
Provision-->Terraform
Configure-->Ansible
Geo-->Ansible
QA-->gagq
subgraph "omnibus-gitlab-mirror"
GET:Geo
end
subgraph getcg [GitLab-environment-toolkit-configs/Geo]
direction LR
Generate-terraform-config-->Provision
Provision-->Generate-ansible-config
Generate-ansible-config-->Configure
Configure-->Geo
Geo-->QA
QA-->Destroy-geo
end
subgraph get [GitLab Environment Toolkit]
Terraform
Ansible
end
subgraph GitLab QA
gagq[GitLab QA Geo Scenario]
end
```

View file

@ -50,6 +50,12 @@ GitLab self-monitoring gives administrators of self-hosted GitLab instances the
**Planned removal milestone: 15.0 (2022-05-22)**
### Move `custom_hooks_dir` setting from GitLab Shell to Gitaly
The [`custom_hooks_dir`](https://docs.gitlab.com/ee/administration/server_hooks.html#create-a-global-server-hook-for-all-repositories) setting is now configured in Gitaly, and will be removed from GitLab Shell in GitLab 15.0.
**Planned removal milestone: 15.0 ()**
## 14.8
### Changes to the `CI_JOB_JWT`

View file

@ -148,9 +148,22 @@ If you get this error, [check the batched background migration options](../user/
### What do I do if my background migrations are stuck?
WARNING:
The following operations can disrupt your GitLab performance.
The following operations can disrupt your GitLab performance. They run a number of Sidekiq jobs that perform various database or file updates.
It is safe to re-execute these commands, especially if you have 1000+ pending jobs which would likely overflow your runtime memory.
#### Background migrations remain in the Sidekiq queue
Run the following check. If it returns non-zero and the count does not decrease over time, follow the rest of the steps in this section.
```shell
# For Omnibus installations:
sudo gitlab-rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
# For installations from source:
cd /home/git/gitlab
sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::BackgroundMigration.remaining'
```
It is safe to re-execute the following commands, especially if you have 1000+ pending jobs which would likely overflow your runtime memory.
**For Omnibus installations**
@ -176,7 +189,52 @@ pending_job_classes = scheduled_queue.select { |job| job["class"] == "Background
pending_job_classes.each { |job_class| Gitlab::BackgroundMigration.steal(job_class) }
```
**Batched migrations (GitLab 14.0 and newer):**
#### Background migrations stuck in 'pending' state
GitLab 13.6 introduced an issue where a background migration named `BackfillJiraTrackerDeploymentType2` can be permanently stuck in a **pending** state across upgrades. To clean up this stuck migration, see the [13.6.0 version-specific instructions](#1360).
For other background migrations stuck in pending, run the following check. If it returns non-zero and the count does not decrease over time, follow the rest of the steps in this section.
```shell
# For Omnibus installations:
sudo gitlab-rails runner -e production 'puts Gitlab::Database::BackgroundMigrationJob.pending.count'
# For installations from source:
cd /home/git/gitlab
sudo -u git -H bundle exec rails runner -e production 'puts Gitlab::Database::BackgroundMigrationJob.pending.count'
```
It is safe to re-attempt these migrations to clear them out from a pending status:
**For Omnibus installations**
```shell
# Start the rails console
sudo gitlab-rails c
# Execute the following in the rails console
Gitlab::Database::BackgroundMigrationJob.pending.find_each do |job|
puts "Running pending job '#{job.class_name}' with arguments #{job.arguments}"
result = Gitlab::BackgroundMigration.perform(job.class_name, job.arguments)
puts "Result: #{result}"
end
```
**For installations from source**
```shell
# Start the rails console
sudo -u git -H bundle exec rails RAILS_ENV=production
# Execute the following in the rails console
Gitlab::Database::BackgroundMigrationJob.pending.find_each do |job|
puts "Running pending job '#{job.class_name}' with arguments #{job.arguments}"
result = Gitlab::BackgroundMigration.perform(job.class_name, job.arguments)
puts "Result: #{result}"
end
```
#### Batched migrations (GitLab 14.0 and later)
See [troubleshooting batched background migrations](../user/admin_area/monitoring/background_migrations.md#troubleshooting).
@ -622,6 +680,19 @@ Ruby 2.7.2 is required. GitLab does not start with Ruby 2.6.6 or older versions.
The required Git version is Git v2.29 or higher.
GitLab 13.6 includes a
[background migration `BackfillJiraTrackerDeploymentType2`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46368)
that may remain stuck permanently in a **pending** state despite completion of work
due to a bug.
To clean up this stuck job, run the following in the [GitLab Rails Console](../administration/operations/rails_console.md):
```ruby
Gitlab::Database::BackgroundMigrationJob.pending.where(class_name: "BackfillJiraTrackerDeploymentType2").find_each do |job|
puts Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded("BackfillJiraTrackerDeploymentType2", job.arguments)
end
```
### 13.4.0
GitLab 13.4.0 includes a background migration to [move all remaining repositories in legacy storage to hashed storage](../administration/raketasks/storage.md#migrate-to-hashed-storage). There are [known issues with this migration](https://gitlab.com/gitlab-org/gitlab/-/issues/259605) which are fixed in GitLab 13.5.4 and later. If possible, skip 13.4.0 and upgrade to 13.5.4 or higher instead. Note that the migration can take quite a while to run, depending on how many repositories must be moved. Be sure to check that all background migrations have completed before upgrading further.

View file

@ -55,8 +55,8 @@ Users with the at least the Maintainer role on a group can create subgroups imme
To create a subgroup:
1. On the top bar, select **Menu > Groups** and find the parent group.
1. In the top right, select **New subgroup**.
1. On the top bar, select **Menu > Groups** and find and select the parent group to add a subgroup to.
1. On the parent group's overview page, in the top right, select **New subgroup**.
1. Select **Create group**.
1. Fill in the fields. View a list of [reserved names](../../reserved_names.md) that cannot be used as group names.
1. Select **Create group**.

View file

@ -50,34 +50,41 @@ module Gitlab
Gitlab::Database::SharedModel.using_connection(connection, &block)
end
def steal(steal_class, retry_dead_jobs: false)
with_shared_connection do
def pending_jobs(include_dead_jobs: false)
Enumerator.new do |y|
queues = [
Sidekiq::ScheduledSet.new,
Sidekiq::Queue.new(self.queue)
]
if retry_dead_jobs
if include_dead_jobs
queues << Sidekiq::RetrySet.new
queues << Sidekiq::DeadSet.new
end
queues.each do |queue|
queue.each do |job|
migration_class, migration_args = job.args
y << job if job.klass == worker_class.name
end
end
end
end
next unless job.klass == worker_class.name
next unless migration_class == steal_class
next if block_given? && !(yield job)
def steal(steal_class, retry_dead_jobs: false)
with_shared_connection do
pending_jobs(include_dead_jobs: retry_dead_jobs).each do |job|
migration_class, migration_args = job.args
begin
perform(migration_class, migration_args) if job.delete
rescue Exception # rubocop:disable Lint/RescueException
worker_class # enqueue this migration again
.perform_async(migration_class, migration_args)
next unless migration_class == steal_class
next if block_given? && !(yield job)
raise
end
begin
perform(migration_class, migration_args) if job.delete
rescue Exception # rubocop:disable Lint/RescueException
worker_class # enqueue this migration again
.perform_async(migration_class, migration_args)
raise
end
end
end

View file

@ -0,0 +1,49 @@
# frozen_string_literal: true
module Gitlab
module Database
module Migrations
class TestBackgroundRunner
# TODO - build a rake task to call this method, and support it in the gitlab-com-database-testing project.
# Until then, we will inject a migration with a very high timestamp during database testing
# that calls this class to run jobs
# See https://gitlab.com/gitlab-org/database-team/gitlab-com-database-testing/-/issues/41 for details
def initialize
@job_coordinator = Gitlab::BackgroundMigration.coordinator_for_database(Gitlab::Database::MAIN_DATABASE_NAME)
end
def traditional_background_migrations
@job_coordinator.pending_jobs
end
def run_jobs(for_duration:)
jobs_to_run = traditional_background_migrations.group_by { |j| class_name_for_job(j) }
return if jobs_to_run.empty?
# without .to_f, we do integer division
# For example, 3.minutes / 2 == 1.minute whereas 3.minutes / 2.to_f == (1.minute + 30.seconds)
duration_per_migration_type = for_duration / jobs_to_run.count.to_f
jobs_to_run.each do |_migration_name, jobs|
run_until = duration_per_migration_type.from_now
jobs.shuffle.each do |j|
break if run_until <= Time.current
run_job(j)
end
end
end
private
def run_job(job)
Gitlab::BackgroundMigration.perform(job.args[0], job.args[1])
end
def class_name_for_job(job)
job.args[0]
end
end
end
end
end

View file

@ -17,21 +17,13 @@ module Gitlab
strong_memoize(:generic_keyset_pagination_has_next_page) do
if before
# If `before` is specified, that points to a specific record,
# even if it's the last one. Since we're asking for `before`,
# then the specific record we're pointing to is in the
# next page
true
elsif first
case sliced_nodes
when Array
sliced_nodes.size > limit_value
else
# If we count the number of requested items plus one (`limit_value + 1`),
# then if we get `limit_value + 1` then we know there is a next page
sliced_nodes.limit(1).offset(limit_value).exists?
# replacing relation count
# relation_count(set_limit(sliced_nodes, limit_value + 1)) == limit_value + 1
end
else
false

View file

@ -14955,9 +14955,15 @@ msgstr ""
msgid "ExternalAuthorization|URL to which the projects make authorization requests. If the URL is blank, cross-project features are available and can still specify classification labels for projects."
msgstr ""
msgid "ExternalIssueIntegration|Another issue tracker is already in use"
msgstr ""
msgid "ExternalIssueIntegration|Not all data may be displayed here. To view more details or make changes to this issue, go to %{linkStart}%{trackerName}%{linkEnd}."
msgstr ""
msgid "ExternalIssueIntegration|Only one issue tracker integration can be active at a time. Please disable the active tracker first and try again."
msgstr ""
msgid "ExternalIssueIntegration|This issue is synchronized with %{trackerName}"
msgstr ""

View file

@ -6,7 +6,7 @@ RSpec.describe Projects::EnvironmentsController do
include MetricsDashboardHelpers
include KubernetesHelpers
let_it_be(:project) { create(:project) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:maintainer) { create(:user, name: 'main-dos').tap { |u| project.add_maintainer(u) } }
let_it_be(:reporter) { create(:user, name: 'repo-dos').tap { |u| project.add_reporter(u) } }
@ -55,11 +55,11 @@ RSpec.describe Projects::EnvironmentsController do
let(:environments) { json_response['environments'] }
context 'with default parameters' do
before do
get :index, params: environment_params(format: :json)
end
subject { get :index, params: environment_params(format: :json) }
it 'responds with a flat payload describing available environments' do
subject
expect(environments.count).to eq 3
expect(environments.first).to include('name' => 'production', 'name_without_type' => 'production')
expect(environments.second).to include('name' => 'staging/review-1', 'name_without_type' => 'review-1')
@ -69,9 +69,28 @@ RSpec.describe Projects::EnvironmentsController do
end
it 'sets the polling interval header' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Poll-Interval']).to eq("3000")
end
context 'validates latest deployment' do
let_it_be(:test_environment) do
create(:environment, project: project, name: 'staging/review-4', state: :available)
end
before do
create_list(:deployment, 2, :success, environment: test_environment, project: project)
end
it 'responds with the latest deployment for the environment' do
subject
environment = environments.find { |env| env['id'] == test_environment.id }
expect(environment['last_deployment']['id']).to eq(test_environment.deployments.last.id)
end
end
end
context 'when a folder-based nested structure is requested' do

View file

@ -224,6 +224,12 @@ FactoryBot.define do
recipients { 'test@example.com' }
end
factory :pivotaltracker_integration, class: 'Integrations::Pivotaltracker' do
project
active { true }
token { 'test' }
end
# this is for testing storing values inside properties, which is deprecated and will be removed in
# https://gitlab.com/gitlab-org/gitlab/issues/29404
trait :without_properties_callback do

View file

@ -35,6 +35,15 @@ describe('ActiveCheckbox', () => {
});
});
describe('when activateDisabled is true', () => {
it('renders GlFormCheckbox as disabled', () => {
createComponent({ activateDisabled: true });
expect(findGlFormCheckbox().exists()).toBe(true);
expect(findInputInCheckbox().attributes('disabled')).toBe('disabled');
});
});
describe('initialActivated is `false`', () => {
beforeEach(() => {
createComponent({

View file

@ -55,6 +55,7 @@ RSpec.describe IntegrationsHelper do
:id,
:show_active,
:activated,
:activate_disabled,
:type,
:merge_request_events,
:commit_events,

View file

@ -38,13 +38,67 @@ RSpec.describe Gitlab::BackgroundMigration::JobCoordinator do
end
end
describe '#pending_jobs' do
context 'when there are enqueued jobs' do
let(:queue) do
[
instance_double(Sidekiq::JobRecord, args: [1, 'queue'], klass: worker_class.name),
instance_double(Sidekiq::JobRecord, args: [2, 'queue'], klass: worker_class.name)
]
end
let(:queue_incorrect_job_class) do
[
instance_double(Sidekiq::JobRecord, args: [1, 'queue'], klass: 'SomeOtherClass')
]
end
let(:scheduled_set) do
[instance_double(Sidekiq::JobRecord, args: [3, 'scheduled'], klass: worker_class.name)]
end
let(:retry_set) do
[instance_double(Sidekiq::JobRecord, args: [4, 'retry'], klass: worker_class.name)]
end
let(:dead_set) do
[instance_double(Sidekiq::JobRecord, args: [5, 'dead'], klass: worker_class.name)]
end
before do
allow(Sidekiq::Queue).to receive(:new)
.with(coordinator.queue)
.and_return(queue + queue_incorrect_job_class)
allow(Sidekiq::ScheduledSet).to receive(:new).and_return(scheduled_set)
allow(Sidekiq::RetrySet).to receive(:new).and_return(retry_set)
allow(Sidekiq::DeadSet).to receive(:new).and_return(dead_set)
end
it 'does not include jobs for other workers' do
expect(coordinator.pending_jobs).not_to include(queue_incorrect_job_class.first)
end
context 'when not including dead jobs' do
it 'includes current and future jobs' do
expect(coordinator.pending_jobs(include_dead_jobs: false).to_a).to match_array(queue + scheduled_set)
end
end
context 'when including dead jobs' do
it 'includes current and future jobs, and also dead and retry jobs' do
expect(coordinator.pending_jobs(include_dead_jobs: true).to_a).to match_array(queue + scheduled_set + retry_set + dead_set)
end
end
end
end
describe '#steal' do
context 'when there are enqueued jobs present' do
let(:queue) do
[
double(args: ['Foo', [10, 20]], klass: worker_class.name),
double(args: ['Bar', [20, 30]], klass: worker_class.name),
double(args: ['Foo', [20, 30]], klass: 'MergeWorker')
instance_double(Sidekiq::JobRecord, args: ['Foo', [10, 20]], klass: worker_class.name),
instance_double(Sidekiq::JobRecord, args: ['Bar', [20, 30]], klass: worker_class.name),
instance_double(Sidekiq::JobRecord, args: ['Foo', [20, 30]], klass: 'MergeWorker')
]
end

View file

@ -0,0 +1,120 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::TestBackgroundRunner, :redis do
include Gitlab::Database::Migrations::BackgroundMigrationHelpers
# In order to test the interaction between queueing sidekiq jobs and seeing those jobs in queues,
# we need to disable sidekiq's testing mode and actually send our jobs to redis
around do |ex|
Sidekiq::Testing.disable! { ex.run }
end
context 'without jobs to run' do
it 'returns immediately' do
runner = described_class.new
expect(runner).not_to receive(:run_job)
described_class.new.run_jobs(for_duration: 1.second)
end
end
context 'with jobs to run' do
let(:migration_name) { 'TestBackgroundMigration' }
before do
(1..5).each do |i|
migrate_in(i.minutes, migration_name, [i])
end
end
context 'finding pending background jobs' do
it 'finds all the migrations' do
expect(described_class.new.traditional_background_migrations.to_a.size).to eq(5)
end
end
context 'running migrations', :freeze_time do
def define_background_migration(name)
klass = Class.new do
# Can't simply def perform here as we won't have access to the block,
# similarly can't define_method(:perform, &block) here as it would change the block receiver
define_method(:perform) { |*args| yield(*args) }
end
stub_const("Gitlab::BackgroundMigration::#{name}", klass)
klass
end
def expect_migration_call_counts(migrations_to_calls)
migrations_to_calls.each do |migration, calls|
expect_next_instances_of(migration, calls) do |m|
expect(m).to receive(:perform).and_call_original
end
end
end
it 'runs the migration class correctly' do
calls = []
define_background_migration(migration_name) do |i|
calls << i
end
described_class.new.run_jobs(for_duration: 1.second) # Any time would work here as we do not advance time
expect(calls).to contain_exactly(1, 2, 3, 4, 5)
end
it 'runs the migration for a uniform amount of time' do
migration = define_background_migration(migration_name) do |i|
travel(1.minute)
end
expect_migration_call_counts(migration => 3)
described_class.new.run_jobs(for_duration: 3.minutes)
end
context 'with multiple migrations to run' do
let(:other_migration_name) { 'OtherBackgroundMigration' }
before do
(1..5).each do |i|
migrate_in(i.minutes, other_migration_name, [i])
end
end
it 'splits the time between migrations when all migrations use all their time' do
migration = define_background_migration(migration_name) do |i|
travel(1.minute)
end
other_migration = define_background_migration(other_migration_name) do |i|
travel(2.minutes)
end
expect_migration_call_counts(
migration => 2, # 1 minute jobs for 90 seconds, can finish the first and start the second
other_migration => 1 # 2 minute jobs for 90 seconds, past deadline after a single job
)
described_class.new.run_jobs(for_duration: 3.minutes)
end
it 'does not give leftover time to extra migrations' do
# This is currently implemented this way for simplicity, but it could make sense to change this behavior.
migration = define_background_migration(migration_name) do
travel(1.second)
end
other_migration = define_background_migration(other_migration_name) do
travel(1.minute)
end
expect_migration_call_counts(
migration => 5,
other_migration => 2
)
described_class.new.run_jobs(for_duration: 3.minutes)
end
end
end
end
end

View file

@ -3,12 +3,12 @@
require 'spec_helper'
RSpec.describe Integrations::BaseIssueTracker do
let(:integration) { Integrations::Redmine.new(project: project, active: true, issue_tracker_data: build(:issue_tracker_data)) }
let_it_be_with_refind(:project) { create :project }
describe 'Validations' do
let(:project) { create :project }
describe 'only one issue tracker per project' do
let(:integration) { Integrations::Redmine.new(project: project, active: true, issue_tracker_data: build(:issue_tracker_data)) }
before do
create(:custom_issue_tracker_integration, project: project)
end
@ -31,4 +31,18 @@ RSpec.describe Integrations::BaseIssueTracker do
end
end
end
describe '#activate_disabled_reason' do
subject { integration.activate_disabled_reason }
context 'when there is an existing issue tracker integration' do
let_it_be(:custom_tracker) { create(:custom_issue_tracker_integration, project: project) }
it { is_expected.to eq(trackers: [custom_tracker]) }
end
context 'when there is no existing issue tracker integration' do
it { is_expected.to be(nil) }
end
end
end

View file

@ -3225,52 +3225,44 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
context 'when failed' do
shared_examples 'failed skip_ci_check' do
context 'when #mergeable_ci_state? is false' do
before do
allow(subject).to receive(:mergeable_ci_state?) { false }
end
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
end
it 'returns true when skipping ci check' do
expect(subject.mergeable_state?(skip_ci_check: true)).to be(true)
end
end
context 'when #mergeable_discussions_state? is false' do
before do
allow(subject).to receive(:mergeable_discussions_state?) { false }
end
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
end
it 'returns true when skipping discussions check' do
expect(subject.mergeable_state?(skip_discussions_check: true)).to be(true)
end
end
end
context 'when improved_mergeability_checks is on' do
it_behaves_like 'failed skip_ci_check'
end
context 'when improved_mergeability_checks is off' do
context 'when #mergeable_ci_state? is false' do
before do
stub_feature_flags(improved_mergeability_checks: false)
allow(subject).to receive(:mergeable_ci_state?) { false }
end
it_behaves_like 'failed skip_ci_check'
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
end
it 'returns true when skipping ci check' do
expect(subject.mergeable_state?(skip_ci_check: true)).to be(true)
end
end
context 'when #mergeable_discussions_state? is false' do
before do
allow(subject).to receive(:mergeable_discussions_state?) { false }
end
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
end
it 'returns true when skipping discussions check' do
expect(subject.mergeable_state?(skip_discussions_check: true)).to be(true)
end
end
end
end
describe '#mergeable_state?' do
context 'when merge state caching is on' do
it_behaves_like 'for mergeable_state'
context 'when improved_mergeability_checks is off' do
before do
stub_feature_flags(improved_mergeability_checks: false)
end
it_behaves_like 'for mergeable_state'
end

View file

@ -204,6 +204,25 @@ RSpec.describe EnvironmentSerializer do
json
end
# Validates possible bug that can arise when order_by is not honoured in the preloader.
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/353966#note_861381504
it 'fetches the last and upcoming deployment correctly' do
last_deployment = nil
upcoming_deployment = nil
create(:environment, project: project).tap do |environment|
create(:deployment, :success, environment: environment, project: project)
last_deployment = create(:deployment, :success, environment: environment, project: project)
create(:deployment, :running, environment: environment, project: project)
upcoming_deployment = create(:deployment, :running, environment: environment, project: project)
end
response_json = json
expect(response_json.last[:last_deployment][:id]).to eq(last_deployment.id)
expect(response_json.last[:upcoming_deployment][:id]).to eq(upcoming_deployment.id)
end
end
def create_environment_with_associations(project)

View file

@ -0,0 +1,43 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe MergeRequests::Mergeability::CheckBrokenStatusService do
subject(:check_broken_status) { described_class.new(merge_request: merge_request, params: {}) }
let(:merge_request) { build(:merge_request) }
describe '#execute' do
before do
expect(merge_request).to receive(:broken?).and_return(broken)
end
context 'when the merge request is broken' do
let(:broken) { true }
it 'returns a check result with status failed' do
expect(check_broken_status.execute.status).to eq Gitlab::MergeRequests::Mergeability::CheckResult::FAILED_STATUS
end
end
context 'when the merge request is not broken' do
let(:broken) { false }
it 'returns a check result with status success' do
expect(check_broken_status.execute.status).to eq Gitlab::MergeRequests::Mergeability::CheckResult::SUCCESS_STATUS
end
end
end
describe '#skip?' do
it 'returns false' do
expect(check_broken_status.skip?).to eq false
end
end
describe '#cacheable?' do
it 'returns false' do
expect(check_broken_status.cacheable?).to eq false
end
end
end

View file

@ -47,7 +47,7 @@ RSpec.describe MergeRequests::Mergeability::RunChecksService do
expect(service).not_to receive(:execute)
end
expect(execute).to match_array([success_result])
expect(execute).to match_array([success_result, success_result])
end
end