Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-06 21:07:50 +00:00
parent 971f05815d
commit c47ade2adb
41 changed files with 580 additions and 119 deletions

View File

@ -87,20 +87,22 @@ gemnasium-python-dependency_scanning:
# Analyze dependencies for malicious behavior
# See https://gitlab.com/gitlab-com/gl-security/security-research/package-hunter
package_hunter:
.package_hunter-base:
extends:
- .default-retry
- .reports:rules:package_hunter
stage: test
image:
name: registry.gitlab.com/gitlab-com/gl-security/security-research/package-hunter-cli:latest
entrypoint: [""]
variables:
DEBUG: '*'
HTR_user: '$PACKAGE_HUNTER_USER'
HTR_pass: '$PACKAGE_HUNTER_PASS'
needs: []
allow_failure: true
script:
before_script:
- rm -r spec locale .git app/assets/images doc/
- cd .. && tar -I "gzip --best" -cf gitlab.tgz gitlab/
- DEBUG=* HTR_user=$PACKAGE_HUNTER_USER HTR_pass=$PACKAGE_HUNTER_PASS node /usr/src/app/cli.js analyze --format gitlab gitlab.tgz | tee $CI_PROJECT_DIR/gl-dependency-scanning-report.json
artifacts:
paths:
- gl-dependency-scanning-report.json
@ -108,6 +110,20 @@ package_hunter:
dependency_scanning: gl-dependency-scanning-report.json
expire_in: 1 week
package_hunter-yarn:
extends:
- .package_hunter-base
- .reports:rules:package_hunter-yarn
script:
- node /usr/src/app/cli.js analyze --format gitlab --manager yarn gitlab.tgz | tee $CI_PROJECT_DIR/gl-dependency-scanning-report.json
package_hunter-bundler:
extends:
- .package_hunter-base
- .reports:rules:package_hunter-bundler
script:
- node /usr/src/app/cli.js analyze --format gitlab --manager bundler gitlab.tgz | tee $CI_PROJECT_DIR/gl-dependency-scanning-report.json
license_scanning:
extends: .default-retry
needs: []

View File

@ -1099,7 +1099,7 @@
- <<: *if-default-branch-schedule-nightly
allow_failure: true
.reports:rules:package_hunter:
.reports:rules:package_hunter-yarn:
rules:
- if: "$PACKAGE_HUNTER_USER == null || $PACKAGE_HUNTER_USER == ''"
when: never
@ -1107,6 +1107,14 @@
- <<: *if-merge-request
changes: ["yarn.lock"]
.reports:rules:package_hunter-bundler:
rules:
- if: "$PACKAGE_HUNTER_USER == null || $PACKAGE_HUNTER_USER == ''"
when: never
- <<: *if-default-branch-schedule-2-hourly
- <<: *if-merge-request
changes: ["Gemfile.lock"]
.reports:rules:license_scanning:
rules:
- if: '$LICENSE_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\blicense_scanning\b/'

View File

@ -30,8 +30,8 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
## Maintainer checklist
- [ ] Correct milestone is applied and the title is matching across all backports
- [ ] Assigned to `@gitlab-release-tools-bot` with passing CI pipelines and **when all backports including the MR targeting master are ready.**
- [ ] Correct milestone is applied and the title is matching across all backports.
- [ ] Assigned to `@gitlab-release-tools-bot` with passing CI pipelines.
/label ~security

View File

@ -164,7 +164,7 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 2.0.10'
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
gem 'asciidoctor-plantuml', '~> 0.0.12'
gem 'asciidoctor-kroki', '~> 0.4.0', require: false
gem 'asciidoctor-kroki', '~> 0.5.0', require: false
gem 'rouge', '~> 3.26.0'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0'

View File

@ -92,10 +92,10 @@ GEM
faraday_middleware (~> 1.0)
faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.4)
asciidoctor (2.0.12)
asciidoctor (2.0.15)
asciidoctor-include-ext (0.3.1)
asciidoctor (>= 1.5.6, < 3.0.0)
asciidoctor-kroki (0.4.0)
asciidoctor-kroki (0.5.0)
asciidoctor (~> 2.0)
asciidoctor-plantuml (0.0.12)
asciidoctor (>= 1.5.6, < 3.0.0)
@ -1409,7 +1409,7 @@ DEPENDENCIES
asana (~> 0.10.3)
asciidoctor (~> 2.0.10)
asciidoctor-include-ext (~> 0.3.1)
asciidoctor-kroki (~> 0.4.0)
asciidoctor-kroki (~> 0.5.0)
asciidoctor-plantuml (~> 0.0.12)
atlassian-jwt (~> 0.2.0)
attr_encrypted (~> 3.1.0)

View File

@ -93,9 +93,8 @@ export default {
tooltip: '',
attrs: {
'data-qa-selector': 'edit_button',
'data-track-event': 'click_edit',
// eslint-disable-next-line @gitlab/require-i18n-strings
'data-track-label': 'Edit',
'data-track-action': 'click_consolidated_edit',
'data-track-label': 'edit',
},
...handleOptions,
};
@ -127,9 +126,8 @@ export default {
tooltip: '',
attrs: {
'data-qa-selector': 'web_ide_button',
'data-track-event': 'click_edit_ide',
// eslint-disable-next-line @gitlab/require-i18n-strings
'data-track-label': 'Web IDE',
'data-track-action': 'click_consolidated_edit_ide',
'data-track-label': 'web_ide',
},
...handleOptions,
};

View File

@ -65,7 +65,7 @@ module BlobHelper
return unless blob = readable_blob(options, path, project, ref)
common_classes = "btn gl-button btn-confirm js-edit-blob gl-ml-3 #{options[:extra_class]}"
data = { track_event: 'click_edit', track_label: 'Edit' }
data = { track_action: 'click_edit', track_label: 'edit' }
if Feature.enabled?(:web_ide_primary_edit, project.group)
common_classes += " btn-inverted"
@ -85,7 +85,7 @@ module BlobHelper
return unless blob
common_classes = 'btn gl-button btn-confirm ide-edit-button gl-ml-3'
data = { track_event: 'click_edit_ide', track_label: 'Web IDE' }
data = { track_action: 'click_edit_ide', track_label: 'web_ide' }
unless Feature.enabled?(:web_ide_primary_edit, project.group)
common_classes += " btn-inverted"

View File

@ -1,32 +1,14 @@
# frozen_string_literal: true
module IntegrationsHelper
def integration_event_description(event)
case event
when "push", "push_events"
s_("ProjectService|Trigger event for pushes to the repository.")
when "tag_push", "tag_push_events"
s_("ProjectService|Trigger event for new tags pushed to the repository.")
when "note", "note_events"
s_("ProjectService|Trigger event for new comments.")
when "confidential_note", "confidential_note_events"
s_("ProjectService|Trigger event for new comments on confidential issues.")
when "issue", "issue_events"
s_("ProjectService|Trigger event when an issue is created, updated, or closed.")
when "confidential_issue", "confidential_issue_events"
s_("ProjectService|Trigger event when a confidential issue is created, updated, or closed.")
when "merge_request", "merge_request_events"
s_("ProjectService|Trigger event when a merge request is created, updated, or merged.")
when "pipeline", "pipeline_events"
s_("ProjectService|Trigger event when a pipeline status changes.")
when "wiki_page", "wiki_page_events"
s_("ProjectService|Trigger event when a wiki page is created or updated.")
when "commit", "commit_events"
s_("ProjectService|Trigger event when a commit is created or updated.")
when "deployment"
s_("ProjectService|Trigger event when a deployment starts or finishes.")
when "alert"
s_("ProjectService|Trigger event when a new, unique alert is recorded.")
def integration_event_description(integration, event)
case integration
when Integrations::Jira
jira_integration_event_description(event)
when Integrations::Teamcity
teamcity_integration_event_description(event)
else
default_integration_event_description(event)
end
end
@ -144,6 +126,53 @@ module IntegrationsHelper
private
def jira_integration_event_description(event)
case event
when "merge_request", "merge_request_events"
s_("JiraService|Jira comments are created when an issue is referenced in a merge request.")
when "commit", "commit_events"
s_("JiraService|Jira comments are created when an issue is referenced in a commit.")
end
end
def teamcity_integration_event_description(event)
case event
when 'push', 'push_events'
s_('TeamcityIntegration|Trigger TeamCity CI after every push to the repository, except branch delete')
when 'merge_request', 'merge_request_events'
s_('TeamcityIntegration|Trigger TeamCity CI after a merge request has been created or updated')
end
end
def default_integration_event_description(event)
case event
when "push", "push_events"
s_("ProjectService|Trigger event for pushes to the repository.")
when "tag_push", "tag_push_events"
s_("ProjectService|Trigger event for new tags pushed to the repository.")
when "note", "note_events"
s_("ProjectService|Trigger event for new comments.")
when "confidential_note", "confidential_note_events"
s_("ProjectService|Trigger event for new comments on confidential issues.")
when "issue", "issue_events"
s_("ProjectService|Trigger event when an issue is created, updated, or closed.")
when "confidential_issue", "confidential_issue_events"
s_("ProjectService|Trigger event when a confidential issue is created, updated, or closed.")
when "merge_request", "merge_request_events"
s_("ProjectService|Trigger event when a merge request is created, updated, or merged.")
when "pipeline", "pipeline_events"
s_("ProjectService|Trigger event when a pipeline status changes.")
when "wiki_page", "wiki_page_events"
s_("ProjectService|Trigger event when a wiki page is created or updated.")
when "commit", "commit_events"
s_("ProjectService|Trigger event when a commit is created or updated.")
when "deployment"
s_("ProjectService|Trigger event when a deployment starts or finishes.")
when "alert"
s_("ProjectService|Trigger event when a new, unique alert is recorded.")
end
end
def trigger_events_for_integration(integration)
ServiceEventSerializer.new(service: integration).represent(integration.configurable_events).to_json
end

View File

@ -172,10 +172,6 @@ class Integration < ApplicationRecord
'push'
end
def self.event_description(event)
IntegrationsHelper.integration_event_description(event)
end
def self.find_or_create_templates
create_nonexistent_templates
for_template

View File

@ -577,15 +577,6 @@ module Integrations
data_fields.deployment_server!
end
end
def self.event_description(event)
case event
when "merge_request", "merge_request_events"
s_("JiraService|Jira comments are created when an issue is referenced in a merge request.")
when "commit", "commit_events"
s_("JiraService|Jira comments are created when an issue is referenced in a commit.")
end
end
end
end

View File

@ -29,15 +29,6 @@ module Integrations
def supported_events
%w(push merge_request)
end
def event_description(event)
case event
when 'push', 'push_events'
'TeamCity CI will be triggered after every push to the repository except branch delete'
when 'merge_request', 'merge_request_events'
'TeamCity CI will be triggered after a merge request has been created or updated'
end
end
end
def compose_service_hook

View File

@ -14,7 +14,7 @@ class ServiceEventEntity < Grape::Entity
end
expose :description do |event|
IntegrationsHelper.integration_event_description(event)
IntegrationsHelper.integration_event_description(integration, event)
end
expose :field, if: -> (_, _) { event_field } do

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
module ServicePing
class BuildPayloadService
def execute
return {} unless allowed_to_report?
raw_payload
end
private
def allowed_to_report?
product_intelligence_enabled? && !User.single_user&.requires_usage_stats_consent?
end
def product_intelligence_enabled?
::Gitlab::CurrentSettings.usage_ping_enabled?
end
def raw_payload
@raw_payload ||= ::Gitlab::UsageData.data(force_refresh: true)
end
end
end
ServicePing::BuildPayloadService.prepend_mod_with('ServicePing::BuildPayloadService')

View File

@ -0,0 +1,8 @@
---
name: load_balancing_refine_load_balancer_methods
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65356
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/335109
milestone: '14.1'
type: development
group: group::memory
default_enabled: false

View File

@ -1,20 +0,0 @@
# frozen_string_literal: true
# Ensure that locked attributes can not be changed using a counter.
# TODO: this can be removed once `asciidoctor` gem is > 2.0.12
# and https://github.com/asciidoctor/asciidoctor/issues/3939 is merged
module Asciidoctor
module DocumentPatch
def counter(name, seed = nil)
return @parent_document.counter(name, seed) if @parent_document # rubocop: disable Gitlab/ModuleWithInstanceVariables
unless attribute_locked? name
super
end
end
end
end
class Asciidoctor::Document
prepend Asciidoctor::DocumentPatch
end

View File

@ -237,3 +237,17 @@ For installations from source:
```shell
RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:packages:migrate
```
You can optionally track progress and verify that all packages migrated successfully.
From the [PostgreSQL console](https://docs.gitlab.com/omnibus/settings/database.html#connecting-to-the-bundled-postgresql-database)
(`sudo gitlab-psql -d gitlabhq_production` for Omnibus GitLab), verify that `objectstg` below (where
`file_store=2`) has the count of all packages:
```shell
gitlabhq_production=# SELECT count(*) AS total, sum(case when file_store = '1' then 1 else 0 end) AS filesystem, sum(case when file_store = '2' then 1 else 0 end) AS objectstg FROM packages_package_files;
total | filesystem | objectstg
------+------------+-----------
34 | 0 | 34
```

View File

@ -93,6 +93,7 @@ are very appreciative of the work done by translators and proofreaders!
- Portuguese, Brazilian
- Paulo George Gomes Bezerra - [GitLab](https://gitlab.com/paulobezerra), [CrowdIn](https://crowdin.com/profile/paulogomes.rep)
- André Gama - [GitLab](https://gitlab.com/andregamma), [CrowdIn](https://crowdin.com/profile/ToeOficial)
- Eduardo Addad de Oliveira - [GitLab](https://gitlab.com/eduardoaddad), [CrowdIn](https://crowdin.com/profile/eduardoaddad)
- Romanian
- Proofreaders needed.
- Russian

View File

@ -134,6 +134,9 @@ If you are not using the GitHub integration, you can still perform an authorizat
1. Hit the **List Your GitHub Repositories** button and wait while GitLab reads your repositories' information.
Once done, you'll be taken to the importer page to select the repositories to import.
To use a newer personal access token in imports after previously performing these steps, sign out of
your GitLab account and sign in again, or revoke the older personal access token in GitHub.
### Select which repositories to import
After you have authorized access to your GitHub repositories, you are redirected to the GitHub importer page and

View File

@ -23,14 +23,14 @@ module API
INTEGRATIONS = integrations.freeze
integration_classes.each do |service|
event_names = service.try(:event_names) || next
integration_classes.each do |integration|
event_names = integration.try(:event_names) || next
event_names.each do |event_name|
INTEGRATIONS[service.to_param.tr("_", "-")] << {
INTEGRATIONS[integration.to_param.tr("_", "-")] << {
required: false,
name: event_name.to_sym,
type: String,
desc: service.event_description(event_name)
desc: IntegrationsHelper.integration_event_description(integration, event_name)
}
end
end

View File

@ -44,6 +44,52 @@ module Gitlab
# TODO: Switch to individual job interval (prereq: https://gitlab.com/gitlab-org/gitlab/-/issues/328801)
duration.to_f / batched_migration.interval
end
def split_and_retry!
with_lock do
raise 'Only failed jobs can be split' unless failed?
new_batch_size = batch_size / 2
raise 'Job cannot be split further' if new_batch_size < 1
batching_strategy = batched_migration.batch_class.new
next_batch_bounds = batching_strategy.next_batch(
batched_migration.table_name,
batched_migration.column_name,
batch_min_value: min_value,
batch_size: new_batch_size
)
midpoint = next_batch_bounds.last
# We don't want the midpoint to go over the existing max_value because
# those IDs would already be in the next batched migration job.
# This could happen when a lot of records in the current batch are deleted.
#
# In this case, we just lower the batch size so that future calls to this
# method could eventually split the job if it continues to fail.
if midpoint >= max_value
update!(batch_size: new_batch_size, status: :pending)
else
old_max_value = max_value
update!(
batch_size: new_batch_size,
max_value: midpoint,
attempts: 0,
status: :pending,
started_at: nil,
finished_at: nil,
metrics: {}
)
new_record = dup
new_record.min_value = midpoint.next
new_record.max_value = old_max_value
new_record.save!
end
end
end
end
end
end

View File

@ -165,6 +165,7 @@ module Gitlab
# while we only need a single host: https://gitlab.com/gitlab-org/gitlab/-/issues/326125#note_615271604
# Also, shuffling the list afterwards doesn't seem to be necessary.
# This may be improved by merging this method with `select_up_to_date_host`.
# Could be removed when `:load_balancing_refine_load_balancer_methods` FF is rolled out
def select_caught_up_hosts(location)
all_hosts = @host_list.hosts
valid_hosts = all_hosts.select { |host| host.caught_up?(location) }
@ -201,6 +202,7 @@ module Gitlab
true
end
# Could be removed when `:load_balancing_refine_load_balancer_methods` FF is rolled out
def set_consistent_hosts_for_request(hosts)
RequestStore[VALID_HOSTS_CACHE_KEY] = hosts
end

View File

@ -53,8 +53,14 @@ module Gitlab
# write location. If no such location exists, err on the side of caution.
return false unless location
load_balancer.select_caught_up_hosts(location).tap do |selected|
unstick(namespace, id) if selected
if ::Feature.enabled?(:load_balancing_refine_load_balancer_methods)
load_balancer.select_up_to_date_host(location).tap do |selected|
unstick(namespace, id) if selected
end
else
load_balancer.select_caught_up_hosts(location).tap do |selected|
unstick(namespace, id) if selected
end
end
end

View File

@ -16,14 +16,20 @@ module Gitlab
# end
class << self
def start(&block)
return @metric_start&.call unless block_given?
@metric_start = block
end
def finish(&block)
return @metric_finish&.call unless block_given?
@metric_finish = block
end
def relation(&block)
return @metric_relation&.call unless block_given?
@metric_relation = block
end
@ -32,15 +38,21 @@ module Gitlab
@column = column
end
attr_reader :metric_operation, :metric_relation, :metric_start, :metric_finish, :column
def cache_start_and_finish_as(cache_key)
@cache_key = cache_key
end
attr_reader :metric_operation, :metric_relation, :metric_start, :metric_finish, :column, :cache_key
end
def value
start, finish = get_or_cache_batch_ids
method(self.class.metric_operation)
.call(relation,
self.class.column,
start: self.class.metric_start&.call,
finish: self.class.metric_finish&.call)
start: start,
finish: finish)
end
def to_sql
@ -73,6 +85,22 @@ module Gitlab
raise "Unknown time frame: #{time_frame} for DatabaseMetric"
end
end
def get_or_cache_batch_ids
return [self.class.start, self.class.finish] unless self.class.cache_key.present?
key_name = "metric_instrumentation/#{self.class.cache_key}"
start = Gitlab::Cache.fetch_once("#{key_name}_minimum_id", expires_in: 1.day) do
self.class.start
end
finish = Gitlab::Cache.fetch_once("#{key_name}_maximum_id", expires_in: 1.day) do
self.class.finish
end
[start, finish]
end
end
end
end

View File

@ -31905,6 +31905,12 @@ msgstr ""
msgid "Team domain"
msgstr ""
msgid "TeamcityIntegration|Trigger TeamCity CI after a merge request has been created or updated"
msgstr ""
msgid "TeamcityIntegration|Trigger TeamCity CI after every push to the repository, except branch delete"
msgstr ""
msgid "Telephone number"
msgstr ""

View File

@ -59,7 +59,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.202.0",
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "30.2.0",
"@gitlab/ui": "30.2.1",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "6.1.3-2",
"@rails/ujs": "6.1.3-2",

View File

@ -156,7 +156,6 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ
<!---->
</div>
</ul>
</div>

View File

@ -176,7 +176,6 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
<!---->
</div>
</ul>
</div>
</td>
@ -304,7 +303,6 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
<!---->
</div>
</ul>
</div>
</td>

View File

@ -15,8 +15,8 @@ const ACTION_EDIT = {
tooltip: '',
attrs: {
'data-qa-selector': 'edit_button',
'data-track-event': 'click_edit',
'data-track-label': 'Edit',
'data-track-action': 'click_consolidated_edit',
'data-track-label': 'edit',
},
};
const ACTION_EDIT_CONFIRM_FORK = {
@ -32,8 +32,8 @@ const ACTION_WEB_IDE = {
text: 'Web IDE',
attrs: {
'data-qa-selector': 'web_ide_button',
'data-track-event': 'click_edit_ide',
'data-track-label': 'Web IDE',
'data-track-action': 'click_consolidated_edit_ide',
'data-track-label': 'web_ide',
},
};
const ACTION_WEB_IDE_CONFIRM_FORK = {

View File

@ -67,8 +67,8 @@ RSpec.describe BlobHelper do
it 'passes on primary tracking attributes' do
parsed_link = Capybara.string(link).find_link('Edit')
expect(parsed_link[:'data-track-event']).to eq("click_edit")
expect(parsed_link[:'data-track-label']).to eq("Edit")
expect(parsed_link[:'data-track-action']).to eq("click_edit")
expect(parsed_link[:'data-track-label']).to eq("edit")
expect(parsed_link[:'data-track-property']).to eq(nil)
end
end
@ -85,8 +85,8 @@ RSpec.describe BlobHelper do
it 'passes on secondary tracking attributes' do
parsed_link = Capybara.string(link).find_link('Edit')
expect(parsed_link[:'data-track-event']).to eq("click_edit")
expect(parsed_link[:'data-track-label']).to eq("Edit")
expect(parsed_link[:'data-track-action']).to eq("click_edit")
expect(parsed_link[:'data-track-label']).to eq("edit")
expect(parsed_link[:'data-track-property']).to eq("secondary")
end
end
@ -332,8 +332,8 @@ RSpec.describe BlobHelper do
it 'passes on secondary tracking attributes' do
parsed_link = Capybara.string(link).find_link('Web IDE')
expect(parsed_link[:'data-track-event']).to eq("click_edit_ide")
expect(parsed_link[:'data-track-label']).to eq("Web IDE")
expect(parsed_link[:'data-track-action']).to eq("click_edit_ide")
expect(parsed_link[:'data-track-label']).to eq("web_ide")
expect(parsed_link[:'data-track-property']).to eq("secondary")
end
end
@ -350,8 +350,8 @@ RSpec.describe BlobHelper do
it 'passes on primary tracking attributes' do
parsed_link = Capybara.string(link).find_link('Web IDE')
expect(parsed_link[:'data-track-event']).to eq("click_edit_ide")
expect(parsed_link[:'data-track-label']).to eq("Web IDE")
expect(parsed_link[:'data-track-action']).to eq("click_edit_ide")
expect(parsed_link[:'data-track-label']).to eq("web_ide")
expect(parsed_link[:'data-track-property']).to eq(nil)
end
end

View File

@ -3,6 +3,22 @@
require 'spec_helper'
RSpec.describe IntegrationsHelper do
describe '#integration_event_description' do
subject(:description) { helper.integration_event_description(integration, 'merge_request_events') }
context 'when integration is Jira' do
let(:integration) { Integrations::Jira.new }
it { is_expected.to include('Jira') }
end
context 'when integration is Team City' do
let(:integration) { Integrations::Teamcity.new }
it { is_expected.to include('TeamCity') }
end
end
describe '#integration_form_data' do
let(:fields) do
[

View File

@ -124,4 +124,73 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
end
end
describe '#split_and_retry!' do
let!(:job) { create(:batched_background_migration_job, batch_size: 10, min_value: 6, max_value: 15, status: :failed) }
it 'splits the job into two and marks them as pending' do
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
end
expect { job.split_and_retry! }.to change { described_class.count }.by(1)
expect(job).to have_attributes(
min_value: 6,
max_value: 10,
batch_size: 5,
status: 'pending',
attempts: 0,
started_at: nil,
finished_at: nil,
metrics: {}
)
new_job = described_class.last
expect(new_job).to have_attributes(
batched_background_migration_id: job.batched_background_migration_id,
min_value: 11,
max_value: 15,
batch_size: 5,
status: 'pending',
attempts: 0,
started_at: nil,
finished_at: nil,
metrics: {}
)
expect(new_job.created_at).not_to eq(job.created_at)
end
context 'when job is not failed' do
let!(:job) { create(:batched_background_migration_job, status: :succeeded) }
it 'raises an exception' do
expect { job.split_and_retry! }.to raise_error 'Only failed jobs can be split'
end
end
context 'when batch size is already 1' do
let!(:job) { create(:batched_background_migration_job, batch_size: 1, status: :failed) }
it 'raises an exception' do
expect { job.split_and_retry! }.to raise_error 'Job cannot be split further'
end
end
context 'when computed midpoint is larger than the max value of the batch' do
before do
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 16])
end
end
it 'lowers the batch size and marks the job as pending' do
expect { job.split_and_retry! }.not_to change { described_class.count }
expect(job.batch_size).to eq(5)
expect(job.status).to eq('pending')
end
end
end
end

View File

@ -325,10 +325,22 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do
end
it 'returns true, selects hosts, and unsticks if any secondary has caught up' do
expect(lb).to receive(:select_caught_up_hosts).and_return(true)
expect(lb).to receive(:select_up_to_date_host).and_return(true)
expect(described_class).to receive(:unstick).with(:project, 42)
expect(described_class.select_caught_up_replicas(:project, 42)).to be true
end
context 'when :load_balancing_refine_load_balancer_methods FF is disabled' do
before do
stub_feature_flags(load_balancing_refine_load_balancer_methods: false)
end
it 'returns true, selects hosts, and unsticks if any secondary has caught up' do
expect(lb).to receive(:select_caught_up_hosts).and_return(true)
expect(described_class).to receive(:unstick).with(:project, 42)
expect(described_class.select_caught_up_replicas(:project, 42)).to be true
end
end
end
end
end

View File

@ -6,7 +6,7 @@ RSpec.describe Gitlab::Kroki do
describe '.formats' do
def default_formats
%w[bytefield c4plantuml ditaa erd graphviz nomnoml plantuml svgbob umlet vega vegalite wavedrom].freeze
%w[bytefield c4plantuml ditaa erd graphviz nomnoml pikchr plantuml svgbob umlet vega vegalite wavedrom].freeze
end
subject { described_class.formats(Gitlab::CurrentSettings) }

View File

@ -0,0 +1,75 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
subject do
described_class.tap do |m|
m.relation { Issue }
m.operation :count
m.start { m.relation.minimum(:id) }
m.finish { m.relation.maximum(:id) }
end.new(time_frame: 'all')
end
describe '#value' do
let_it_be(:issue_1) { create(:issue) }
let_it_be(:issue_2) { create(:issue) }
let_it_be(:issue_3) { create(:issue) }
let_it_be(:issues) { Issue.all }
before do
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
end
it 'calculates a correct result' do
expect(subject.value).to eq(3)
end
it 'does not cache the result of start and finish', :request_store, :use_clean_rails_redis_caching do
expect(Gitlab::Cache).not_to receive(:fetch_once)
expect(subject).to receive(:count).with(any_args, hash_including(start: issues.min_by(&:id).id, finish: issues.max_by(&:id).id)).and_call_original
subject.value
expect(Rails.cache.read('metric_instrumentation/special_issue_count_minimum_id')).to eq(nil)
expect(Rails.cache.read('metric_instrumentation/special_issue_count_maximum_id')).to eq(nil)
end
context 'with start and finish not called' do
subject do
described_class.tap do |m|
m.relation { Issue }
m.operation :count
end.new(time_frame: 'all')
end
it 'calculates a correct result' do
expect(subject.value).to eq(3)
end
end
context 'with cache_start_and_finish_as called' do
subject do
described_class.tap do |m|
m.relation { Issue }
m.operation :count
m.start { m.relation.minimum(:id) }
m.finish { m.relation.maximum(:id) }
m.cache_start_and_finish_as :special_issue_count
end.new(time_frame: 'all')
end
it 'caches using the key name passed', :request_store, :use_clean_rails_redis_caching do
expect(Gitlab::Cache).to receive(:fetch_once).with('metric_instrumentation/special_issue_count_minimum_id', any_args).and_call_original
expect(Gitlab::Cache).to receive(:fetch_once).with('metric_instrumentation/special_issue_count_maximum_id', any_args).and_call_original
expect(subject).to receive(:count).with(any_args, hash_including(start: issues.min_by(&:id).id, finish: issues.max_by(&:id).id)).and_call_original
subject.value
expect(Rails.cache.read('metric_instrumentation/special_issue_count_minimum_id')).to eq(issues.min_by(&:id).id)
expect(Rails.cache.read('metric_instrumentation/special_issue_count_maximum_id')).to eq(issues.max_by(&:id).id)
end
end
end
end

View File

@ -0,0 +1,47 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ServicePing::BuildPayloadService do
describe '#execute', :without_license do
subject(:service_ping_payload) { described_class.new.execute }
include_context 'stubbed service ping metrics definitions' do
let(:subscription_metrics) do
[
metric_attributes('active_user_count', "Subscription")
]
end
end
context 'when usage_ping_enabled setting is false' do
before do
# Gitlab::CurrentSettings.usage_ping_enabled? == false
stub_config_setting(usage_ping_enabled: false)
end
it 'returns empty service ping payload' do
expect(service_ping_payload).to eq({})
end
end
context 'when usage_ping_enabled setting is true' do
before do
# Gitlab::CurrentSettings.usage_ping_enabled? == true
stub_config_setting(usage_ping_enabled: true)
end
it_behaves_like 'complete service ping payload'
context 'with require stats consent enabled' do
before do
allow(User).to receive(:single_user).and_return(double(:user, requires_usage_stats_consent?: true))
end
it 'returns empty service ping payload' do
expect(service_ping_payload).to eq({})
end
end
end
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
RSpec::Matchers.define :have_usage_metric do |key_path|
match do |payload|
payload = payload.deep_stringify_keys
key_path.split('.').each do |part|
break false unless payload&.has_key?(part)
payload = payload[part]
end
end
failure_message do
"Payload does not contain metric with key path: '#{key_path}'"
end
failure_message_when_negated do
"Payload contains restricted metric with key path: '#{key_path}'"
end
end

View File

@ -0,0 +1,43 @@
# frozen_string_literal: true
RSpec.shared_context 'stubbed service ping metrics definitions' do
include UsageDataHelpers
let(:metrics_definitions) { standard_metrics + subscription_metrics + operational_metrics + optional_metrics }
let(:standard_metrics) do
[
metric_attributes('uuid', "Standard")
]
end
let(:operational_metrics) do
[
metric_attributes('counts.merge_requests', "Operational"),
metric_attributes('counts.todos', "Operational")
]
end
let(:optional_metrics) do
[
metric_attributes('counts.boards', "Optional"),
metric_attributes('gitaly.filesystems', '').except('data_category')
]
end
before do
stub_usage_data_connections
stub_object_store_settings
allow(Gitlab::Usage::MetricDefinition).to(
receive(:definitions)
.and_return(metrics_definitions.to_h { |definition| [definition['key_path'], Gitlab::Usage::MetricDefinition.new('', definition.symbolize_keys)] })
)
end
def metric_attributes(key_path, category)
{
'key_path' => key_path,
'data_category' => category
}
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
RSpec.shared_examples 'complete service ping payload' do
it_behaves_like 'service ping payload with all expected metrics' do
let(:expected_metrics) do
standard_metrics + subscription_metrics + operational_metrics + optional_metrics
end
end
end

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
RSpec.shared_examples 'service ping payload with all expected metrics' do
specify do
aggregate_failures do
expected_metrics.each do |metric|
is_expected.to have_usage_metric metric['key_path']
end
end
end
end

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
RSpec.shared_examples 'service ping payload without restricted metrics' do
specify do
aggregate_failures do
restricted_metrics.each do |metric|
is_expected.not_to have_usage_metric metric['key_path']
end
end
end
end

View File

@ -908,10 +908,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@30.2.0":
version "30.2.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-30.2.0.tgz#eceec947f901cca9507a1ac8b3bd0031a5dcacf9"
integrity sha512-rYG3HyUHZQyum9+6OKvp45r9b9E/wzAl8rpFyIIZMg6a14JPfsGhdjXqycWlLxf3TAsbTD6MtjQm/z/I8J6V8g==
"@gitlab/ui@30.2.1":
version "30.2.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-30.2.1.tgz#be582413712cd2372ff01279e47579f5785591d4"
integrity sha512-Pv2w5ZSR7+G5zcaRjvf8KPs7wQRBqAXXNvKOw2pg/aZsRoK+JfN1neNmfeaqO6K/k1TJyiP6inDXAvhU8SqmOg==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "2.18.1"