Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6f2065c468
commit
5d75b2b9a9
2
Gemfile
2
Gemfile
|
@ -425,7 +425,7 @@ gem 'gitlab-mail_room', '~> 0.0.3', require: 'mail_room'
|
|||
gem 'email_reply_trimmer', '~> 0.1'
|
||||
gem 'html2text'
|
||||
|
||||
gem 'ruby-prof', '~> 1.0.0'
|
||||
gem 'ruby-prof', '~> 1.3.0'
|
||||
gem 'stackprof', '~> 0.2.15', require: false
|
||||
gem 'rbtrace', '~> 0.4', require: false
|
||||
gem 'memory_profiler', '~> 0.9', require: false
|
||||
|
|
|
@ -951,7 +951,7 @@ GEM
|
|||
i18n
|
||||
ruby-fogbugz (0.2.1)
|
||||
crack (~> 0.4)
|
||||
ruby-prof (1.0.0)
|
||||
ruby-prof (1.3.1)
|
||||
ruby-progressbar (1.10.1)
|
||||
ruby-saml (1.7.2)
|
||||
nokogiri (>= 1.5.10)
|
||||
|
@ -1358,7 +1358,7 @@ DEPENDENCIES
|
|||
rubocop-performance (~> 1.4.1)
|
||||
rubocop-rspec (~> 1.37.0)
|
||||
ruby-fogbugz (~> 0.2.1)
|
||||
ruby-prof (~> 1.0.0)
|
||||
ruby-prof (~> 1.3.0)
|
||||
ruby-progressbar
|
||||
ruby_parser (~> 3.8)
|
||||
rubyzip (~> 2.0.0)
|
||||
|
|
|
@ -68,6 +68,9 @@ module BulkInsertSafe
|
|||
# @param [Boolean] validate Whether validations should run on [items]
|
||||
# @param [Integer] batch_size How many items should at most be inserted at once
|
||||
# @param [Boolean] skip_duplicates Marks duplicates as allowed, and skips inserting them
|
||||
# @param [Symbol] returns Pass :ids to return an array with the primary key values
|
||||
# for all inserted records or nil to omit the underlying
|
||||
# RETURNING SQL clause entirely.
|
||||
# @param [Proc] handle_attributes Block that will receive each item attribute hash
|
||||
# prior to insertion for further processing
|
||||
#
|
||||
|
@ -78,10 +81,11 @@ module BulkInsertSafe
|
|||
#
|
||||
# @return true if operation succeeded, throws otherwise.
|
||||
#
|
||||
def bulk_insert!(items, validate: true, skip_duplicates: false, batch_size: DEFAULT_BATCH_SIZE, &handle_attributes)
|
||||
def bulk_insert!(items, validate: true, skip_duplicates: false, returns: nil, batch_size: DEFAULT_BATCH_SIZE, &handle_attributes)
|
||||
_bulk_insert_all!(items,
|
||||
validate: validate,
|
||||
on_duplicate: skip_duplicates ? :skip : :raise,
|
||||
returns: returns,
|
||||
unique_by: nil,
|
||||
batch_size: batch_size,
|
||||
&handle_attributes)
|
||||
|
@ -94,6 +98,9 @@ module BulkInsertSafe
|
|||
# @param [Boolean] validate Whether validations should run on [items]
|
||||
# @param [Integer] batch_size How many items should at most be inserted at once
|
||||
# @param [Symbol/Array] unique_by Defines index or columns to use to consider item duplicate
|
||||
# @param [Symbol] returns Pass :ids to return an array with the primary key values
|
||||
# for all inserted or updated records or nil to omit the
|
||||
# underlying RETURNING SQL clause entirely.
|
||||
# @param [Proc] handle_attributes Block that will receive each item attribute hash
|
||||
# prior to insertion for further processing
|
||||
#
|
||||
|
@ -109,10 +116,11 @@ module BulkInsertSafe
|
|||
#
|
||||
# @return true if operation succeeded, throws otherwise.
|
||||
#
|
||||
def bulk_upsert!(items, unique_by:, validate: true, batch_size: DEFAULT_BATCH_SIZE, &handle_attributes)
|
||||
def bulk_upsert!(items, unique_by:, returns: nil, validate: true, batch_size: DEFAULT_BATCH_SIZE, &handle_attributes)
|
||||
_bulk_insert_all!(items,
|
||||
validate: validate,
|
||||
on_duplicate: :update,
|
||||
returns: returns,
|
||||
unique_by: unique_by,
|
||||
batch_size: batch_size,
|
||||
&handle_attributes)
|
||||
|
@ -120,21 +128,30 @@ module BulkInsertSafe
|
|||
|
||||
private
|
||||
|
||||
def _bulk_insert_all!(items, on_duplicate:, unique_by:, validate:, batch_size:, &handle_attributes)
|
||||
return true if items.empty?
|
||||
def _bulk_insert_all!(items, on_duplicate:, returns:, unique_by:, validate:, batch_size:, &handle_attributes)
|
||||
return [] if items.empty?
|
||||
|
||||
returning =
|
||||
case returns
|
||||
when :ids
|
||||
[primary_key]
|
||||
when nil
|
||||
false
|
||||
else
|
||||
raise ArgumentError, "returns needs to be :ids or nil"
|
||||
end
|
||||
|
||||
transaction do
|
||||
items.each_slice(batch_size) do |item_batch|
|
||||
items.each_slice(batch_size).flat_map do |item_batch|
|
||||
attributes = _bulk_insert_item_attributes(
|
||||
item_batch, validate, &handle_attributes)
|
||||
|
||||
ActiveRecord::InsertAll
|
||||
.new(self, attributes, on_duplicate: on_duplicate, unique_by: unique_by)
|
||||
.new(self, attributes, on_duplicate: on_duplicate, returning: returning, unique_by: unique_by)
|
||||
.execute
|
||||
.pluck(primary_key)
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def _bulk_insert_item_attributes(items, validate_items)
|
||||
|
|
|
@ -58,7 +58,7 @@ module Projects
|
|||
end
|
||||
|
||||
def tree_saver_class
|
||||
if ::Feature.enabled?(:streaming_serializer, project)
|
||||
if ::Feature.enabled?(:streaming_serializer, project, default_enabled: true)
|
||||
Gitlab::ImportExport::Project::TreeSaver
|
||||
else
|
||||
# Once we remove :streaming_serializer feature flag, Project::LegacyTreeSaver should be removed as well
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Projects
|
||||
module Prometheus
|
||||
module Alerts
|
||||
class NotifyService < BaseService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include IncidentManagement::Settings
|
||||
|
||||
def execute(token)
|
||||
return false unless valid_payload_size?
|
||||
return false unless valid_version?
|
||||
return false unless valid_alert_manager_token?(token)
|
||||
|
||||
persist_events
|
||||
send_alert_email if send_email?
|
||||
process_incident_issues if process_issues?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_payload_size?
|
||||
Gitlab::Utils::DeepSize.new(params).valid?
|
||||
end
|
||||
|
||||
def send_email?
|
||||
incident_management_setting.send_email && firings.any?
|
||||
end
|
||||
|
||||
def firings
|
||||
@firings ||= alerts_by_status('firing')
|
||||
end
|
||||
|
||||
def alerts_by_status(status)
|
||||
alerts.select { |alert| alert['status'] == status }
|
||||
end
|
||||
|
||||
def alerts
|
||||
params['alerts']
|
||||
end
|
||||
|
||||
def valid_version?
|
||||
params['version'] == '4'
|
||||
end
|
||||
|
||||
def valid_alert_manager_token?(token)
|
||||
valid_for_manual?(token) || valid_for_managed?(token)
|
||||
end
|
||||
|
||||
def valid_for_manual?(token)
|
||||
prometheus = project.find_or_initialize_service('prometheus')
|
||||
return false unless prometheus.manual_configuration?
|
||||
|
||||
if setting = project.alerting_setting
|
||||
compare_token(token, setting.token)
|
||||
else
|
||||
token.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def valid_for_managed?(token)
|
||||
prometheus_application = available_prometheus_application(project)
|
||||
return false unless prometheus_application
|
||||
|
||||
if token
|
||||
compare_token(token, prometheus_application.alert_manager_token)
|
||||
else
|
||||
prometheus_application.alert_manager_token.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def available_prometheus_application(project)
|
||||
alert_id = gitlab_alert_id
|
||||
return unless alert_id
|
||||
|
||||
alert = find_alert(project, alert_id)
|
||||
return unless alert
|
||||
|
||||
cluster = alert.environment.deployment_platform&.cluster
|
||||
return unless cluster&.enabled?
|
||||
return unless cluster.application_prometheus_available?
|
||||
|
||||
cluster.application_prometheus
|
||||
end
|
||||
|
||||
def find_alert(project, metric)
|
||||
Projects::Prometheus::AlertsFinder
|
||||
.new(project: project, metric: metric)
|
||||
.execute
|
||||
.first
|
||||
end
|
||||
|
||||
def gitlab_alert_id
|
||||
alerts&.first&.dig('labels', 'gitlab_alert_id')
|
||||
end
|
||||
|
||||
def compare_token(expected, actual)
|
||||
return unless expected && actual
|
||||
|
||||
ActiveSupport::SecurityUtils.secure_compare(expected, actual)
|
||||
end
|
||||
|
||||
def send_alert_email
|
||||
notification_service
|
||||
.async
|
||||
.prometheus_alerts_fired(project, firings)
|
||||
end
|
||||
|
||||
def process_incident_issues
|
||||
alerts.each do |alert|
|
||||
IncidentManagement::ProcessPrometheusAlertWorker
|
||||
.perform_async(project.id, alert.to_h)
|
||||
end
|
||||
end
|
||||
|
||||
def persist_events
|
||||
CreateEventsService.new(project, nil, params).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable streaming serializer feature flag by default.
|
||||
merge_request: 27813
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add support for Okta as a SCIM provider
|
||||
merge_request: 25649
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Patch to use COPY in db/structure.sql when populating schema_migrations table
|
||||
# This is intended to reduce potential for merge conflicts in db/structure.sql
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Database::PostgresqlAdapter::SchemaVersionsCopyMixin)
|
2039
db/structure.sql
2039
db/structure.sql
File diff suppressed because it is too large
Load Diff
|
@ -14,7 +14,7 @@ tasks such as:
|
|||
To request access to Chatops on GitLab.com:
|
||||
|
||||
1. Log into <https://ops.gitlab.net/users/sign_in> **using the same username** as for GitLab.com (you may have to rename it).
|
||||
1. Ask in the [#production](https://gitlab.slack.com/messages/production) channel to add you by running `/chatops run member add <username> gitlab-com/chatops --ops`.
|
||||
1. Ask in the [#production](https://gitlab.slack.com/messages/production) channel for an existing member to add you to the `chatops` project in Ops. They can do it by running `/chatops run member add <username> gitlab-com/chatops --ops` command in that channel.
|
||||
|
||||
NOTE: **Note:** If you had to change your username for GitLab.com on the first step, make sure [to reflect this information](https://gitlab.com/gitlab-com/www-gitlab-com#adding-yourself-to-the-team-page) on [the team page](https://about.gitlab.com/company/team/).
|
||||
|
||||
|
|
|
@ -150,6 +150,10 @@ module API
|
|||
authorize! :download_code, release
|
||||
end
|
||||
|
||||
def authorize_create_evidence!
|
||||
# This is a separate method so that EE can extend its behaviour
|
||||
end
|
||||
|
||||
def release
|
||||
@release ||= user_project.releases.find_by_tag(params[:tag])
|
||||
end
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Database
|
||||
module PostgresqlAdapter
|
||||
module SchemaVersionsCopyMixin
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def dump_schema_information # :nodoc:
|
||||
versions = schema_migration.all_versions
|
||||
copy_versions_sql(versions) if versions.any?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def copy_versions_sql(versions)
|
||||
sm_table = quote_table_name(schema_migration.table_name)
|
||||
|
||||
sql = +"COPY #{sm_table} (version) FROM STDIN;\n"
|
||||
sql << versions.map { |v| Integer(v) }.sort.join("\n")
|
||||
sql << "\n\\.\n"
|
||||
|
||||
sql
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -168,9 +168,9 @@ module Gitlab
|
|||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def self.print_by_total_time(result, options = {})
|
||||
default_options = { sort_method: :total_time }
|
||||
default_options = { sort_method: :total_time, filter_by: :total_time }
|
||||
|
||||
Gitlab::Profiler::TotalTimeFlatPrinter.new(result).print(STDOUT, default_options.merge(options))
|
||||
RubyProf::FlatPrinter.new(result).print(STDOUT, default_options.merge(options))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Profiler
|
||||
class TotalTimeFlatPrinter < RubyProf::FlatPrinter
|
||||
def max_percent
|
||||
@options[:max_percent] || 100
|
||||
end
|
||||
|
||||
# Copied from:
|
||||
# <https://github.com/ruby-prof/ruby-prof/blob/master/lib/ruby-prof/printers/flat_printer.rb>
|
||||
#
|
||||
# The changes are just to filter by total time, not self time, and add a
|
||||
# max_percent option as well.
|
||||
def print_methods(thread)
|
||||
total_time = thread.total_time
|
||||
methods = thread.methods.sort_by(&sort_method).reverse
|
||||
|
||||
sum = 0
|
||||
methods.each do |method|
|
||||
total_percent = (method.total_time / total_time) * 100
|
||||
next if total_percent < min_percent
|
||||
next if total_percent > max_percent
|
||||
|
||||
sum += method.self_time
|
||||
|
||||
@output << "%6.2f %9.3f %9.3f %9.3f %9.3f %8d %s%-30s %s\n" % [
|
||||
method.self_time / total_time * 100, # %self
|
||||
method.total_time, # total
|
||||
method.self_time, # self
|
||||
method.wait_time, # wait
|
||||
method.children_time, # children
|
||||
method.called, # calls
|
||||
method.recursive? ? "*" : " ", # cycle
|
||||
method.full_name, # method_name
|
||||
method_location(method) # location
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,6 +4,32 @@ require 'toml-rb'
|
|||
|
||||
module Gitlab
|
||||
module SetupHelper
|
||||
def create_configuration(dir, storage_paths, force: false)
|
||||
generate_configuration(
|
||||
configuration_toml(dir, storage_paths),
|
||||
get_config_path(dir),
|
||||
force: force
|
||||
)
|
||||
end
|
||||
|
||||
# rubocop:disable Rails/Output
|
||||
def generate_configuration(toml_data, config_path, force: false)
|
||||
FileUtils.rm_f(config_path) if force
|
||||
|
||||
File.open(config_path, File::WRONLY | File::CREAT | File::EXCL) do |f|
|
||||
f.puts toml_data
|
||||
end
|
||||
rescue Errno::EEXIST
|
||||
puts 'Skipping config.toml generation:'
|
||||
puts 'A configuration file already exists.'
|
||||
rescue ArgumentError => e
|
||||
puts 'Skipping config.toml generation:'
|
||||
puts e.message
|
||||
end
|
||||
# rubocop:enable Rails/Output
|
||||
|
||||
module Gitaly
|
||||
extend Gitlab::SetupHelper
|
||||
class << self
|
||||
# We cannot create config.toml files for all possible Gitaly configuations.
|
||||
# For instance, if Gitaly is running on another machine then it makes no
|
||||
|
@ -13,7 +39,7 @@ module Gitlab
|
|||
# because it uses a Unix socket.
|
||||
# For development and testing purposes, an extra storage is added to gitaly,
|
||||
# which is not known to Rails, but must be explicitly stubbed.
|
||||
def gitaly_configuration_toml(gitaly_dir, storage_paths, gitaly_ruby: true)
|
||||
def configuration_toml(gitaly_dir, storage_paths, gitaly_ruby: true)
|
||||
storages = []
|
||||
address = nil
|
||||
|
||||
|
@ -31,13 +57,20 @@ module Gitlab
|
|||
storages << { name: key, path: storage_paths[key] }
|
||||
end
|
||||
|
||||
config = { socket_path: address.sub(/\Aunix:/, '') }
|
||||
|
||||
if Rails.env.test?
|
||||
storage_path = Rails.root.join('tmp', 'tests', 'second_storage').to_s
|
||||
storages << { name: 'test_second_storage', path: storage_path }
|
||||
|
||||
config[:auth] = { token: 'secret' }
|
||||
# Compared to production, tests run in constrained environments. This
|
||||
# number is meant to grow with the number of concurrent rails requests /
|
||||
# sidekiq jobs, and concurrency will be low anyway in test.
|
||||
config[:git] = { catfile_cache_size: 5 }
|
||||
end
|
||||
|
||||
config = { socket_path: address.sub(/\Aunix:/, ''), storage: storages }
|
||||
config[:auth] = { token: 'secret' } if Rails.env.test?
|
||||
config[:storage] = storages
|
||||
|
||||
internal_socket_dir = File.join(gitaly_dir, 'internal_sockets')
|
||||
FileUtils.mkdir(internal_socket_dir) unless File.exist?(internal_socket_dir)
|
||||
|
@ -47,32 +80,34 @@ module Gitlab
|
|||
config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path }
|
||||
config[:bin_dir] = Gitlab.config.gitaly.client_path
|
||||
|
||||
if Rails.env.test?
|
||||
# Compared to production, tests run in constrained environments. This
|
||||
# number is meant to grow with the number of concurrent rails requests /
|
||||
# sidekiq jobs, and concurrency will be low anyway in test.
|
||||
config[:git] = { catfile_cache_size: 5 }
|
||||
TomlRB.dump(config)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_config_path(dir)
|
||||
File.join(dir, 'config.toml')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Praefect
|
||||
extend Gitlab::SetupHelper
|
||||
class << self
|
||||
def configuration_toml(gitaly_dir, storage_paths)
|
||||
nodes = [{ storage: 'default', address: "unix:#{gitaly_dir}/gitaly.socket", primary: true, token: 'secret' }]
|
||||
config = { socket_path: "#{gitaly_dir}/praefect.socket", virtual_storage_name: 'default', token: 'secret', node: nodes }
|
||||
config[:token] = 'secret' if Rails.env.test?
|
||||
|
||||
TomlRB.dump(config)
|
||||
end
|
||||
|
||||
# rubocop:disable Rails/Output
|
||||
def create_gitaly_configuration(dir, storage_paths, force: false)
|
||||
config_path = File.join(dir, 'config.toml')
|
||||
FileUtils.rm_f(config_path) if force
|
||||
private
|
||||
|
||||
File.open(config_path, File::WRONLY | File::CREAT | File::EXCL) do |f|
|
||||
f.puts gitaly_configuration_toml(dir, storage_paths)
|
||||
def get_config_path(dir)
|
||||
File.join(dir, 'praefect.config.toml')
|
||||
end
|
||||
rescue Errno::EEXIST
|
||||
puts "Skipping config.toml generation:"
|
||||
puts "A configuration file already exists."
|
||||
rescue ArgumentError => e
|
||||
puts "Skipping config.toml generation:"
|
||||
puts e.message
|
||||
end
|
||||
# rubocop:enable Rails/Output
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,7 +27,7 @@ Usage: rake "gitlab:gitaly:install[/installation/dir,/storage/path]")
|
|||
end
|
||||
|
||||
storage_paths = { 'default' => args.storage_path }
|
||||
Gitlab::SetupHelper.create_gitaly_configuration(args.dir, storage_paths)
|
||||
Gitlab::SetupHelper::Gitaly.create_configuration(args.dir, storage_paths)
|
||||
Dir.chdir(args.dir) do
|
||||
# In CI we run scripts/gitaly-test-build instead of this command
|
||||
unless ENV['CI'].present?
|
||||
|
|
|
@ -48,7 +48,7 @@ class UploadedFile
|
|||
return if path.blank? && remote_id.blank?
|
||||
|
||||
file_path = nil
|
||||
if path
|
||||
if path.present?
|
||||
file_path = File.realpath(path)
|
||||
|
||||
paths = Array(upload_paths) << Dir.tmpdir
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
"@babel/preset-env": "^7.8.4",
|
||||
"@gitlab/at.js": "^1.5.5",
|
||||
"@gitlab/svgs": "^1.115.0",
|
||||
"@gitlab/ui": "^10.0.0",
|
||||
"@gitlab/ui": "^10.0.1",
|
||||
"@gitlab/visual-review-tools": "1.5.1",
|
||||
"@sentry/browser": "^5.10.2",
|
||||
"@sourcegraph/code-host-integration": "0.0.33",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
context 'Create', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/issues/36817', type: :bug } do
|
||||
context 'Create', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/30226', type: :bug } do
|
||||
describe 'Merge request rebasing' do
|
||||
it 'user rebases source branch of merge request' do
|
||||
Flow::Login.sign_in
|
||||
|
|
|
@ -17,13 +17,16 @@ class GitalyTestBuild
|
|||
check_gitaly_config!
|
||||
|
||||
# Starting gitaly further validates its configuration
|
||||
pid = start_gitaly
|
||||
Process.kill('TERM', pid)
|
||||
gitaly_pid = start_gitaly
|
||||
praefect_pid = start_praefect
|
||||
Process.kill('TERM', gitaly_pid)
|
||||
Process.kill('TERM', praefect_pid)
|
||||
|
||||
# Make the 'gitaly' executable look newer than 'GITALY_SERVER_VERSION'.
|
||||
# Without this a gitaly executable created in the setup-test-env job
|
||||
# will look stale compared to GITALY_SERVER_VERSION.
|
||||
FileUtils.touch(File.join(tmp_tests_gitaly_dir, 'gitaly'), mtime: Time.now + (1 << 24))
|
||||
FileUtils.touch(File.join(tmp_tests_gitaly_dir, 'praefect'), mtime: Time.now + (1 << 24))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -13,10 +13,9 @@ class GitalyTestSpawn
|
|||
# # Uncomment line below to see all gitaly logs merged into CI trace
|
||||
# spawn('sleep 1; tail -f log/gitaly-test.log')
|
||||
|
||||
pid = start_gitaly
|
||||
|
||||
# In local development this pid file is used by rspec.
|
||||
IO.write(File.expand_path('../tmp/tests/gitaly.pid', __dir__), pid)
|
||||
IO.write(File.expand_path('../tmp/tests/gitaly.pid', __dir__), start_gitaly)
|
||||
IO.write(File.expand_path('../tmp/tests/praefect.pid', __dir__), start_praefect)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -37,16 +37,31 @@ module GitalyTest
|
|||
env_hash
|
||||
end
|
||||
|
||||
def config_path
|
||||
def config_path(service)
|
||||
case service
|
||||
when :gitaly
|
||||
File.join(tmp_tests_gitaly_dir, 'config.toml')
|
||||
when :praefect
|
||||
File.join(tmp_tests_gitaly_dir, 'praefect.config.toml')
|
||||
end
|
||||
end
|
||||
|
||||
def start_gitaly
|
||||
args = %W[#{tmp_tests_gitaly_dir}/gitaly #{config_path}]
|
||||
pid = spawn(env, *args, [:out, :err] => 'log/gitaly-test.log')
|
||||
start(:gitaly)
|
||||
end
|
||||
|
||||
def start_praefect
|
||||
start(:praefect)
|
||||
end
|
||||
|
||||
def start(service)
|
||||
args = ["#{tmp_tests_gitaly_dir}/#{service}"]
|
||||
args.push("-config") if service == :praefect
|
||||
args.push(config_path(service))
|
||||
pid = spawn(env, *args, [:out, :err] => "log/#{service}-test.log")
|
||||
|
||||
begin
|
||||
try_connect!
|
||||
try_connect!(service)
|
||||
rescue
|
||||
Process.kill('TERM', pid)
|
||||
raise
|
||||
|
@ -68,11 +83,11 @@ module GitalyTest
|
|||
abort 'bundle check failed' unless system(env, 'bundle', 'check', chdir: File.dirname(gemfile))
|
||||
end
|
||||
|
||||
def read_socket_path
|
||||
def read_socket_path(service)
|
||||
# This code needs to work in an environment where we cannot use bundler,
|
||||
# so we cannot easily use the toml-rb gem. This ad-hoc parser should be
|
||||
# good enough.
|
||||
config_text = IO.read(config_path)
|
||||
config_text = IO.read(config_path(service))
|
||||
|
||||
config_text.lines.each do |line|
|
||||
match_data = line.match(/^\s*socket_path\s*=\s*"([^"]*)"$/)
|
||||
|
@ -80,14 +95,14 @@ module GitalyTest
|
|||
return match_data[1] if match_data
|
||||
end
|
||||
|
||||
raise "failed to find socket_path in #{config_path}"
|
||||
raise "failed to find socket_path in #{config_path(service)}"
|
||||
end
|
||||
|
||||
def try_connect!
|
||||
print "Trying to connect to gitaly: "
|
||||
def try_connect!(service)
|
||||
print "Trying to connect to #{service}: "
|
||||
timeout = 20
|
||||
delay = 0.1
|
||||
socket = read_socket_path
|
||||
socket = read_socket_path(service)
|
||||
|
||||
Integer(timeout / delay).times do
|
||||
UNIXSocket.new(socket)
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Database::PostgresqlAdapter::SchemaVersionsCopyMixin do
|
||||
let(:schema_migration) { double('schem_migration', table_name: table_name, all_versions: versions) }
|
||||
let(:versions) { %w(5 2 1000 200 4 93 2) }
|
||||
let(:table_name) { "schema_migrations" }
|
||||
|
||||
let(:instance) do
|
||||
Object.new.extend(described_class)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(instance).to receive(:schema_migration).and_return(schema_migration)
|
||||
allow(instance).to receive(:quote_table_name).with(table_name).and_return("\"#{table_name}\"")
|
||||
end
|
||||
|
||||
subject { instance.dump_schema_information }
|
||||
|
||||
it 'uses COPY FROM STDIN' do
|
||||
expect(subject.split("\n").first).to match(/COPY "schema_migrations" \(version\) FROM STDIN;/)
|
||||
end
|
||||
|
||||
it 'contains a sorted list of versions by their numeric value' do
|
||||
version_lines = subject.split("\n")[1..-2].map(&:to_i)
|
||||
|
||||
expect(version_lines).to eq(versions.map(&:to_i).sort)
|
||||
end
|
||||
|
||||
it 'contains a end-of-data marker' do
|
||||
expect(subject).to end_with("\\.\n")
|
||||
end
|
||||
|
||||
context 'with non-Integer versions' do
|
||||
let(:versions) { %w(5 2 4 abc) }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject }.to raise_error(/invalid value for Integer/)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -59,6 +59,16 @@ describe UploadedFile do
|
|||
expect(subject.sha256).to eq('sha256')
|
||||
expect(subject.remote_id).to eq('remote_id')
|
||||
end
|
||||
|
||||
it 'handles a blank path' do
|
||||
params['file.path'] = ''
|
||||
|
||||
# Not a real file, so can't determine size itself
|
||||
params['file.size'] = 1.byte
|
||||
|
||||
expect { described_class.from_params(params, :file, upload_path) }
|
||||
.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -129,10 +129,37 @@ describe BulkInsertSafe do
|
|||
end.not_to change { described_class.count }
|
||||
end
|
||||
|
||||
it 'does nothing and returns true when items are empty' do
|
||||
expect(described_class.bulk_insert!([])).to be(true)
|
||||
it 'does nothing and returns an empty array when items are empty' do
|
||||
expect(described_class.bulk_insert!([])).to eq([])
|
||||
expect(described_class.count).to eq(0)
|
||||
end
|
||||
|
||||
context 'with returns option set' do
|
||||
context 'when is set to :ids' do
|
||||
it 'return an array with the primary key values for all inserted records' do
|
||||
items = described_class.valid_list(1)
|
||||
|
||||
expect(described_class.bulk_insert!(items, returns: :ids)).to contain_exactly(a_kind_of(Integer))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when is set to nil' do
|
||||
it 'returns an empty array' do
|
||||
items = described_class.valid_list(1)
|
||||
|
||||
expect(described_class.bulk_insert!(items, returns: nil)).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when is set to anything else' do
|
||||
it 'raises an error' do
|
||||
items = described_class.valid_list(1)
|
||||
|
||||
expect { described_class.bulk_insert!([items], returns: [:id, :name]) }
|
||||
.to raise_error(ArgumentError, "returns needs to be :ids or nil")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when duplicate items are to be inserted' do
|
||||
|
|
|
@ -6,6 +6,7 @@ describe API::Releases do
|
|||
let(:project) { create(:project, :repository, :private) }
|
||||
let(:maintainer) { create(:user) }
|
||||
let(:reporter) { create(:user) }
|
||||
let(:developer) { create(:user) }
|
||||
let(:guest) { create(:user) }
|
||||
let(:non_project_member) { create(:user) }
|
||||
let(:commit) { create(:commit, project: project) }
|
||||
|
@ -15,6 +16,7 @@ describe API::Releases do
|
|||
project.add_maintainer(maintainer)
|
||||
project.add_reporter(reporter)
|
||||
project.add_guest(guest)
|
||||
project.add_developer(developer)
|
||||
|
||||
project.repository.add_tag(maintainer, 'v0.1', commit.id)
|
||||
project.repository.add_tag(maintainer, 'v0.2', commit.id)
|
||||
|
@ -248,6 +250,24 @@ describe API::Releases do
|
|||
.to match_array(release.sources.map(&:url))
|
||||
end
|
||||
|
||||
context 'with evidence' do
|
||||
let!(:evidence) { create(:evidence, release: release) }
|
||||
|
||||
it 'returns the evidence' do
|
||||
get api("/projects/#{project.id}/releases/v0.1", maintainer)
|
||||
|
||||
expect(json_response['evidences'].count).to eq(1)
|
||||
end
|
||||
|
||||
it '#collected_at' do
|
||||
Timecop.freeze(Time.now.round) do
|
||||
get api("/projects/#{project.id}/releases/v0.1", maintainer)
|
||||
|
||||
expect(json_response['evidences'].first['collected_at'].to_datetime.to_i).to be_within(1.minute).of(release.evidences.first.created_at.to_i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when release has link asset' do
|
||||
let!(:link) do
|
||||
create(:release_link,
|
||||
|
|
|
@ -0,0 +1,343 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Projects::Prometheus::Alerts::NotifyService do
|
||||
let_it_be(:project, reload: true) { create(:project) }
|
||||
|
||||
let(:service) { described_class.new(project, nil, payload) }
|
||||
let(:token_input) { 'token' }
|
||||
|
||||
let!(:setting) do
|
||||
create(:project_incident_management_setting, project: project, send_email: true, create_issue: true)
|
||||
end
|
||||
|
||||
let(:subject) { service.execute(token_input) }
|
||||
|
||||
before do
|
||||
# We use `let_it_be(:project)` so we make sure to clear caches
|
||||
project.clear_memoization(:licensed_feature_available)
|
||||
end
|
||||
|
||||
shared_examples 'sends notification email' do
|
||||
let(:notification_service) { spy }
|
||||
|
||||
it 'sends a notification for firing alerts only' do
|
||||
expect(NotificationService)
|
||||
.to receive(:new)
|
||||
.and_return(notification_service)
|
||||
|
||||
expect(notification_service)
|
||||
.to receive_message_chain(:async, :prometheus_alerts_fired)
|
||||
|
||||
expect(subject).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'processes incident issues' do |amount|
|
||||
let(:create_incident_service) { spy }
|
||||
|
||||
it 'processes issues' do
|
||||
expect(IncidentManagement::ProcessPrometheusAlertWorker)
|
||||
.to receive(:perform_async)
|
||||
.with(project.id, kind_of(Hash))
|
||||
.exactly(amount).times
|
||||
|
||||
Sidekiq::Testing.inline! do
|
||||
expect(subject).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'does not process incident issues' do
|
||||
it 'does not process issues' do
|
||||
expect(IncidentManagement::ProcessPrometheusAlertWorker)
|
||||
.not_to receive(:perform_async)
|
||||
|
||||
expect(subject).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'persists events' do
|
||||
let(:create_events_service) { spy }
|
||||
|
||||
it 'persists events' do
|
||||
expect(Projects::Prometheus::Alerts::CreateEventsService)
|
||||
.to receive(:new)
|
||||
.and_return(create_events_service)
|
||||
|
||||
expect(create_events_service)
|
||||
.to receive(:execute)
|
||||
|
||||
expect(subject).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'notifies alerts' do
|
||||
it_behaves_like 'sends notification email'
|
||||
it_behaves_like 'persists events'
|
||||
end
|
||||
|
||||
shared_examples 'no notifications' do
|
||||
let(:notification_service) { spy }
|
||||
let(:create_events_service) { spy }
|
||||
|
||||
it 'does not notify' do
|
||||
expect(notification_service).not_to receive(:async)
|
||||
expect(create_events_service).not_to receive(:execute)
|
||||
|
||||
expect(subject).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with valid payload' do
|
||||
let(:alert_firing) { create(:prometheus_alert, project: project) }
|
||||
let(:alert_resolved) { create(:prometheus_alert, project: project) }
|
||||
let(:payload_raw) { payload_for(firing: [alert_firing], resolved: [alert_resolved]) }
|
||||
let(:payload) { ActionController::Parameters.new(payload_raw).permit! }
|
||||
let(:payload_alert_firing) { payload_raw['alerts'].first }
|
||||
let(:token) { 'token' }
|
||||
|
||||
context 'with project specific cluster' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:cluster_enabled, :status, :configured_token, :token_input, :result) do
|
||||
true | :installed | token | token | :success
|
||||
true | :installed | nil | nil | :success
|
||||
true | :updated | token | token | :success
|
||||
true | :updating | token | token | :failure
|
||||
true | :installed | token | 'x' | :failure
|
||||
true | :installed | nil | token | :failure
|
||||
true | :installed | token | nil | :failure
|
||||
true | nil | token | token | :failure
|
||||
false | :installed | token | token | :failure
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
cluster = create(:cluster, :provided_by_user,
|
||||
projects: [project],
|
||||
enabled: cluster_enabled)
|
||||
|
||||
if status
|
||||
create(:clusters_applications_prometheus, status,
|
||||
cluster: cluster,
|
||||
alert_manager_token: configured_token)
|
||||
end
|
||||
end
|
||||
|
||||
case result = params[:result]
|
||||
when :success
|
||||
it_behaves_like 'notifies alerts'
|
||||
when :failure
|
||||
it_behaves_like 'no notifications'
|
||||
else
|
||||
raise "invalid result: #{result.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without project specific cluster' do
|
||||
let!(:cluster) { create(:cluster, enabled: true) }
|
||||
|
||||
it_behaves_like 'no notifications'
|
||||
end
|
||||
|
||||
context 'with manual prometheus installation' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:alerting_setting, :configured_token, :token_input, :result) do
|
||||
true | token | token | :success
|
||||
true | token | 'x' | :failure
|
||||
true | token | nil | :failure
|
||||
false | nil | nil | :success
|
||||
false | nil | token | :failure
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:alert_manager_token) { token_input }
|
||||
|
||||
before do
|
||||
create(:prometheus_service, project: project)
|
||||
|
||||
if alerting_setting
|
||||
create(:project_alerting_setting,
|
||||
project: project,
|
||||
token: configured_token)
|
||||
end
|
||||
end
|
||||
|
||||
case result = params[:result]
|
||||
when :success
|
||||
it_behaves_like 'notifies alerts'
|
||||
when :failure
|
||||
it_behaves_like 'no notifications'
|
||||
else
|
||||
raise "invalid result: #{result.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'alert emails' do
|
||||
before do
|
||||
create(:prometheus_service, project: project)
|
||||
create(:project_alerting_setting, project: project, token: token)
|
||||
end
|
||||
|
||||
context 'when incident_management_setting does not exist' do
|
||||
let!(:setting) { nil }
|
||||
|
||||
it_behaves_like 'persists events'
|
||||
|
||||
it 'does not send notification email', :sidekiq_might_not_need_inline do
|
||||
expect_any_instance_of(NotificationService)
|
||||
.not_to receive(:async)
|
||||
|
||||
expect(subject).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when incident_management_setting.send_email is true' do
|
||||
it_behaves_like 'notifies alerts'
|
||||
end
|
||||
|
||||
context 'incident_management_setting.send_email is false' do
|
||||
let!(:setting) do
|
||||
create(:project_incident_management_setting, send_email: false, project: project)
|
||||
end
|
||||
|
||||
it_behaves_like 'persists events'
|
||||
|
||||
it 'does not send notification' do
|
||||
expect(NotificationService).not_to receive(:new)
|
||||
|
||||
expect(subject).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'process incident issues' do
|
||||
before do
|
||||
create(:prometheus_service, project: project)
|
||||
create(:project_alerting_setting, project: project, token: token)
|
||||
end
|
||||
|
||||
context 'with create_issue setting enabled' do
|
||||
before do
|
||||
setting.update!(create_issue: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'processes incident issues', 2
|
||||
|
||||
context 'multiple firing alerts' do
|
||||
let(:payload_raw) do
|
||||
payload_for(firing: [alert_firing, alert_firing], resolved: [])
|
||||
end
|
||||
|
||||
it_behaves_like 'processes incident issues', 2
|
||||
end
|
||||
|
||||
context 'without firing alerts' do
|
||||
let(:payload_raw) do
|
||||
payload_for(firing: [], resolved: [alert_resolved])
|
||||
end
|
||||
|
||||
it_behaves_like 'processes incident issues', 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'with create_issue setting disabled' do
|
||||
before do
|
||||
setting.update!(create_issue: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'does not process incident issues'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid payload' do
|
||||
context 'without version' do
|
||||
let(:payload) { {} }
|
||||
|
||||
it_behaves_like 'no notifications'
|
||||
end
|
||||
|
||||
context 'when version is not "4"' do
|
||||
let(:payload) { { 'version' => '5' } }
|
||||
|
||||
it_behaves_like 'no notifications'
|
||||
end
|
||||
|
||||
context 'with missing alerts' do
|
||||
let(:payload) { { 'version' => '4' } }
|
||||
|
||||
it_behaves_like 'no notifications'
|
||||
end
|
||||
|
||||
context 'when the payload is too big' do
|
||||
let(:payload) { { 'the-payload-is-too-big' => true } }
|
||||
let(:deep_size_object) { instance_double(Gitlab::Utils::DeepSize, valid?: false) }
|
||||
|
||||
before do
|
||||
allow(Gitlab::Utils::DeepSize).to receive(:new).and_return(deep_size_object)
|
||||
end
|
||||
|
||||
it_behaves_like 'no notifications'
|
||||
|
||||
it 'does not process issues' do
|
||||
expect(IncidentManagement::ProcessPrometheusAlertWorker)
|
||||
.not_to receive(:perform_async)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def payload_for(firing: [], resolved: [])
|
||||
status = firing.any? ? 'firing' : 'resolved'
|
||||
alerts = firing + resolved
|
||||
alert_name = alerts.first.title
|
||||
prometheus_metric_id = alerts.first.prometheus_metric_id.to_s
|
||||
|
||||
alerts_map = \
|
||||
firing.map { |alert| map_alert_payload('firing', alert) } +
|
||||
resolved.map { |alert| map_alert_payload('resolved', alert) }
|
||||
|
||||
# See https://prometheus.io/docs/alerting/configuration/#%3Cwebhook_config%3E
|
||||
{
|
||||
'version' => '4',
|
||||
'receiver' => 'gitlab',
|
||||
'status' => status,
|
||||
'alerts' => alerts_map,
|
||||
'groupLabels' => {
|
||||
'alertname' => alert_name
|
||||
},
|
||||
'commonLabels' => {
|
||||
'alertname' => alert_name,
|
||||
'gitlab' => 'hook',
|
||||
'gitlab_alert_id' => prometheus_metric_id
|
||||
},
|
||||
'commonAnnotations' => {},
|
||||
'externalURL' => '',
|
||||
'groupKey' => "{}:{alertname=\'#{alert_name}\'}"
|
||||
}
|
||||
end
|
||||
|
||||
def map_alert_payload(status, alert)
|
||||
{
|
||||
'status' => status,
|
||||
'labels' => {
|
||||
'alertname' => alert.title,
|
||||
'gitlab' => 'hook',
|
||||
'gitlab_alert_id' => alert.prometheus_metric_id.to_s
|
||||
},
|
||||
'annotations' => {},
|
||||
'startsAt' => '2018-09-24T08:57:31.095725221Z',
|
||||
'endsAt' => '0001-01-01T00:00:00Z',
|
||||
'generatorURL' => 'http://prometheus-prometheus-server-URL'
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rspec/mocks'
|
||||
require 'toml-rb'
|
||||
|
||||
module TestEnv
|
||||
extend ActiveSupport::Concern
|
||||
|
@ -87,7 +86,7 @@ module TestEnv
|
|||
'conflict-resolvable-fork' => '404fa3f'
|
||||
}.freeze
|
||||
|
||||
TMP_TEST_PATH = Rails.root.join('tmp', 'tests', '**')
|
||||
TMP_TEST_PATH = Rails.root.join('tmp', 'tests').freeze
|
||||
REPOS_STORAGE = 'default'.freeze
|
||||
SECOND_STORAGE_PATH = Rails.root.join('tmp', 'tests', 'second_storage')
|
||||
|
||||
|
@ -140,7 +139,7 @@ module TestEnv
|
|||
#
|
||||
# Keeps gitlab-shell and gitlab-test
|
||||
def clean_test_path
|
||||
Dir[TMP_TEST_PATH].each do |entry|
|
||||
Dir[File.join(TMP_TEST_PATH, '**')].each do |entry|
|
||||
unless test_dirs.include?(File.basename(entry))
|
||||
FileUtils.rm_rf(entry)
|
||||
end
|
||||
|
@ -164,7 +163,8 @@ module TestEnv
|
|||
install_dir: gitaly_dir,
|
||||
version: Gitlab::GitalyClient.expected_server_version,
|
||||
task: "gitlab:gitaly:install[#{install_gitaly_args}]") do
|
||||
Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
|
||||
Gitlab::SetupHelper::Gitaly.create_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
|
||||
Gitlab::SetupHelper::Praefect.create_configuration(gitaly_dir, { 'praefect' => repos_path }, force: true)
|
||||
start_gitaly(gitaly_dir)
|
||||
end
|
||||
end
|
||||
|
@ -192,17 +192,38 @@ module TestEnv
|
|||
end
|
||||
end
|
||||
|
||||
@gitaly_pid = Integer(File.read('tmp/tests/gitaly.pid'))
|
||||
gitaly_pid = Integer(File.read(TMP_TEST_PATH.join('gitaly.pid')))
|
||||
praefect_pid = Integer(File.read(TMP_TEST_PATH.join('praefect.pid')))
|
||||
|
||||
Kernel.at_exit { stop_gitaly }
|
||||
Kernel.at_exit { stop(gitaly_pid) }
|
||||
Kernel.at_exit { stop(praefect_pid) }
|
||||
|
||||
wait_gitaly
|
||||
wait('gitaly')
|
||||
wait('praefect')
|
||||
end
|
||||
|
||||
def wait_gitaly
|
||||
def stop(pid)
|
||||
Process.kill('KILL', pid)
|
||||
rescue Errno::ESRCH
|
||||
# The process can already be gone if the test run was INTerrupted.
|
||||
end
|
||||
|
||||
def gitaly_url
|
||||
ENV.fetch('GITALY_REPO_URL', nil)
|
||||
end
|
||||
|
||||
def socket_path(service)
|
||||
TMP_TEST_PATH.join('gitaly', "#{service}.socket").to_s
|
||||
end
|
||||
|
||||
def praefect_socket_path
|
||||
"unix:" + socket_path(:praefect)
|
||||
end
|
||||
|
||||
def wait(service)
|
||||
sleep_time = 10
|
||||
sleep_interval = 0.1
|
||||
socket = Gitlab::GitalyClient.address('default').sub('unix:', '')
|
||||
socket = socket_path(service)
|
||||
|
||||
Integer(sleep_time / sleep_interval).times do
|
||||
Socket.unix(socket)
|
||||
|
@ -211,19 +232,7 @@ module TestEnv
|
|||
sleep sleep_interval
|
||||
end
|
||||
|
||||
raise "could not connect to gitaly at #{socket.inspect} after #{sleep_time} seconds"
|
||||
end
|
||||
|
||||
def stop_gitaly
|
||||
return unless @gitaly_pid
|
||||
|
||||
Process.kill('KILL', @gitaly_pid)
|
||||
rescue Errno::ESRCH
|
||||
# The process can already be gone if the test run was INTerrupted.
|
||||
end
|
||||
|
||||
def gitaly_url
|
||||
ENV.fetch('GITALY_REPO_URL', nil)
|
||||
raise "could not connect to #{service} at #{socket.inspect} after #{sleep_time} seconds"
|
||||
end
|
||||
|
||||
def setup_workhorse
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'helpers/test_env'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.before(:each, :praefect) do
|
||||
allow(Gitlab.config.repositories.storages['default']).to receive(:[]).and_call_original
|
||||
allow(Gitlab.config.repositories.storages['default']).to receive(:[]).with('gitaly_address')
|
||||
.and_return(TestEnv.praefect_socket_path)
|
||||
end
|
||||
end
|
|
@ -45,11 +45,11 @@ RSpec.shared_examples 'a BulkInsertSafe model' do |klass|
|
|||
expect { target_class.bulk_insert!(items) }.to change { target_class.count }.by(items.size)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
it 'returns an empty array' do
|
||||
items = valid_items_for_bulk_insertion
|
||||
|
||||
expect(items).not_to be_empty
|
||||
expect(target_class.bulk_insert!(items)).to be true
|
||||
expect(target_class.bulk_insert!(items)).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -69,7 +69,7 @@ RSpec.shared_examples 'a BulkInsertSafe model' do |klass|
|
|||
|
||||
# it is not always possible to create invalid items
|
||||
if items.any?
|
||||
expect(target_class.bulk_insert!(items, validate: false)).to be(true)
|
||||
expect(target_class.bulk_insert!(items, validate: false)).to eq([])
|
||||
expect(target_class.count).to eq(items.size)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -786,10 +786,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.115.0.tgz#2762ad045d5a2bd728f74fcb4c00caa9bd6dbc22"
|
||||
integrity sha512-jlmNGqCTpSiPFrNbLaW6GGXNbvIShLdrpeYTtSEz/yFJMClQfPjHc8Zm9bl/PqAM5d/yGQqk8e+rBc4LeAhEfg==
|
||||
|
||||
"@gitlab/ui@^10.0.0":
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-10.0.0.tgz#dced1119237f328367e8c4922cf4e1ae986fac54"
|
||||
integrity sha512-+qsojtfE5mhryjJyReXBY9C3J4s4jlRpHfEcaCFuhcebtq5Uhd6xgLwgxT+E7fMvtLQpGATMo1DiD80yhLb2pQ==
|
||||
"@gitlab/ui@^10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-10.0.1.tgz#65deee8ded3b8d003dfd74cd93c7eb0549e11b37"
|
||||
integrity sha512-RMOJjpZjmWJnu0ebfGJsPOn6/ko+HlfHYbBXBImpTIk6Xsr5AaRjT4yCYEoefZ55jK/SJ2nxHytqrMe26wjfDA==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
|
Loading…
Reference in New Issue