Commit 02dd9b1b authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 3507ddd4 656a918a
......@@ -183,6 +183,7 @@ setup-test-env:
- tmp/tests/gitlab-workhorse/gitlab-workhorse
- tmp/tests/gitlab-workhorse/gitlab-resize-image
- tmp/tests/gitlab-workhorse/config.toml
- tmp/tests/gitlab-workhorse/WORKHORSE_TREE
- tmp/tests/repositories/
- tmp/tests/second_storage/
when: always
......
......@@ -61,7 +61,9 @@ export default {
<template>
<section id="grafana" class="settings no-animate js-grafana-integration">
<div class="settings-header">
<h4 class="js-section-header">
<h4
class="js-section-header settings-title js-settings-toggle js-settings-toggle-trigger-only"
>
{{ s__('GrafanaIntegration|Grafana authentication') }}
</h4>
<gl-button class="js-settings-toggle">{{ __('Expand') }}</gl-button>
......
......@@ -44,13 +44,14 @@ export default {
</script>
<template>
<div class="dropdown">
<div class="js-pipeline-info">
<div class="js-pipeline-info" data-testid="pipeline-info">
<ci-icon :status="pipeline.details.status" class="vertical-align-middle" />
<span class="font-weight-bold">{{ s__('Job|Pipeline') }}</span>
<gl-link
:href="pipeline.path"
class="js-pipeline-path link-commit"
data-testid="pipeline-path"
data-qa-selector="pipeline_path"
>#{{ pipeline.id }}</gl-link
>
......@@ -58,13 +59,17 @@ export default {
{{ s__('Job|for') }}
<template v-if="isTriggeredByMergeRequest">
<gl-link :href="pipeline.merge_request.path" class="link-commit ref-name js-mr-link"
<gl-link
:href="pipeline.merge_request.path"
class="link-commit ref-name"
data-testid="mr-link"
>!{{ pipeline.merge_request.iid }}</gl-link
>
{{ s__('Job|with') }}
<gl-link
:href="pipeline.merge_request.source_branch_path"
class="link-commit ref-name js-source-branch-link"
class="link-commit ref-name"
data-testid="source-branch-link"
>{{ pipeline.merge_request.source_branch }}</gl-link
>
......@@ -72,7 +77,8 @@ export default {
{{ s__('Job|into') }}
<gl-link
:href="pipeline.merge_request.target_branch_path"
class="link-commit ref-name js-target-branch-link"
class="link-commit ref-name"
data-testid="target-branch-link"
>{{ pipeline.merge_request.target_branch }}</gl-link
>
</template>
......
......@@ -2,9 +2,9 @@ import initFilePickers from '~/file_pickers';
import BindInOut from '../../../../behaviors/bind_in_out';
import Group from '../../../../group';
document.addEventListener('DOMContentLoaded', () => {
(() => {
BindInOut.initAll();
initFilePickers();
return new Group();
});
})();
import Labels from '../../../../labels';
document.addEventListener('DOMContentLoaded', () => new Labels());
new Labels(); // eslint-disable-line no-new
......@@ -7,46 +7,44 @@ import { __ } from '~/locale';
import EmojiMenu from './emoji_menu';
const defaultStatusEmoji = 'speech_balloon';
const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu';
const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector);
const statusEmojiField = document.getElementById('js-status-emoji-field');
const statusMessageField = document.getElementById('js-status-message-field');
document.addEventListener('DOMContentLoaded', () => {
const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu';
const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector);
const statusEmojiField = document.getElementById('js-status-emoji-field');
const statusMessageField = document.getElementById('js-status-message-field');
const toggleNoEmojiPlaceholder = (isVisible) => {
const toggleNoEmojiPlaceholder = (isVisible) => {
const placeholderElement = document.getElementById('js-no-emoji-placeholder');
placeholderElement.classList.toggle('hidden', !isVisible);
};
};
const findStatusEmoji = () => toggleEmojiMenuButton.querySelector('gl-emoji');
const removeStatusEmoji = () => {
const findStatusEmoji = () => toggleEmojiMenuButton.querySelector('gl-emoji');
const removeStatusEmoji = () => {
const statusEmoji = findStatusEmoji();
if (statusEmoji) {
statusEmoji.remove();
}
};
};
const selectEmojiCallback = (emoji, emojiTag) => {
const selectEmojiCallback = (emoji, emojiTag) => {
statusEmojiField.value = emoji;
toggleNoEmojiPlaceholder(false);
removeStatusEmoji();
toggleEmojiMenuButton.innerHTML += emojiTag;
};
};
const clearEmojiButton = document.getElementById('js-clear-user-status-button');
clearEmojiButton.addEventListener('click', () => {
const clearEmojiButton = document.getElementById('js-clear-user-status-button');
clearEmojiButton.addEventListener('click', () => {
statusEmojiField.value = '';
statusMessageField.value = '';
removeStatusEmoji();
toggleNoEmojiPlaceholder(true);
});
});
const emojiAutocomplete = new GfmAutoComplete();
emojiAutocomplete.setup($(statusMessageField), { emojis: true });
const emojiAutocomplete = new GfmAutoComplete();
emojiAutocomplete.setup($(statusMessageField), { emojis: true });
const userNameInput = document.getElementById('user_name');
userNameInput.addEventListener('input', () => {
const userNameInput = document.getElementById('user_name');
userNameInput.addEventListener('input', () => {
const EMOJI_REGEX = emojiRegex();
if (EMOJI_REGEX.test(userNameInput.value)) {
// set field to invalid so it gets detected by GlFieldErrors
......@@ -54,9 +52,9 @@ document.addEventListener('DOMContentLoaded', () => {
} else {
userNameInput.setCustomValidity('');
}
});
});
Emoji.initEmojiMap()
Emoji.initEmojiMap()
.then(() => {
const emojiMenu = new EmojiMenu(
Emoji,
......@@ -84,4 +82,3 @@ document.addEventListener('DOMContentLoaded', () => {
});
})
.catch(() => createFlash(__('Failed to load emoji list.')));
});
......@@ -11,7 +11,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
# https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30233
before_action :set_application_setting, except: :integrations
before_action :whitelist_query_limiting, only: [:usage_data]
before_action :disable_query_limiting, only: [:usage_data]
before_action only: [:ci_cd] do
push_frontend_feature_flag(:ci_instance_variables_ui, default_enabled: true)
......@@ -194,8 +194,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
@plans = Plan.all
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/63107')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/63107')
end
def application_setting_params
......
......@@ -4,7 +4,7 @@ class Admin::ServicesController < Admin::ApplicationController
include ServiceParams
before_action :service, only: [:edit, :update]
before_action :whitelist_query_limiting, only: [:index]
before_action :disable_query_limiting, only: [:index]
feature_category :integrations
......@@ -39,7 +39,7 @@ class Admin::ServicesController < Admin::ApplicationController
end
# rubocop: enable CodeReuse/ActiveRecord
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/220357')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/220357')
end
end
......@@ -13,7 +13,7 @@ module Boards
requires_cross_project_access if: -> { board&.group_board? }
before_action :whitelist_query_limiting, only: [:bulk_move]
before_action :disable_query_limiting, only: [:bulk_move]
before_action :authorize_read_issue, only: [:index]
before_action :authorize_create_issue, only: [:create]
before_action :authorize_update_issue, only: [:update]
......@@ -147,8 +147,8 @@ module Boards
serializer.represent(resource, opts)
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/35174')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/35174')
end
def validate_id_list
......
......@@ -4,7 +4,8 @@ class GraphqlController < ApplicationController
# Unauthenticated users have access to the API for public data
skip_before_action :authenticate_user!
WHITELIST_HEADER = 'HTTP_X_GITLAB_QUERY_WHITELIST_ISSUE'
# Header can be passed by tests to disable SQL query limits.
DISABLE_SQL_QUERY_LIMIT_HEADER = 'HTTP_X_GITLAB_DISABLE_SQL_QUERY_LIMIT'
# If a user is using their session to access GraphQL, we need to have session
# storage, since the admin-mode check is session wide.
......@@ -23,7 +24,7 @@ class GraphqlController < ApplicationController
before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
before_action :set_user_last_activity
before_action :track_vs_code_usage
before_action :whitelist_query!
before_action :disable_query_limiting
# Since we deactivate authentication from the main ApplicationController and
# defer it to :authorize_access_api!, we need to override the bypass session
......@@ -62,12 +63,14 @@ class GraphqlController < ApplicationController
private
# Tests may mark some queries as exempt from query limits
def whitelist_query!
whitelist_issue = request.headers[WHITELIST_HEADER]
return unless whitelist_issue
# Tests may mark some GraphQL queries as exempt from SQL query limits
def disable_query_limiting
return unless Gitlab::QueryLimiting.enabled_for_env?
Gitlab::QueryLimiting.whitelist(whitelist_issue)
disable_issue = request.headers[DISABLE_SQL_QUERY_LIMIT_HEADER]
return unless disable_issue
Gitlab::QueryLimiting.disable!(disable_issue)
end
def set_user_last_activity
......
......@@ -3,7 +3,7 @@
class Import::GitlabProjectsController < Import::BaseController
include WorkhorseAuthorization
before_action :whitelist_query_limiting, only: [:create]
before_action :disable_query_limiting, only: [:create]
before_action :verify_gitlab_project_import_enabled
def new
......@@ -42,8 +42,8 @@ class Import::GitlabProjectsController < Import::BaseController
)
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
end
def uploader_class
......
......@@ -3,7 +3,7 @@
class Import::ManifestController < Import::BaseController
extend ::Gitlab::Utils::Override
before_action :whitelist_query_limiting, only: [:create]
before_action :disable_query_limiting, only: [:create]
before_action :verify_import_enabled
before_action :ensure_import_vars, only: [:create, :status]
......@@ -115,7 +115,7 @@ class Import::ManifestController < Import::BaseController
render_404 unless manifest_import_enabled?
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/48939')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/48939')
end
end
......@@ -8,7 +8,7 @@ class Projects::CommitsController < Projects::ApplicationController
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
around_action :allow_gitaly_ref_name_caching
before_action :whitelist_query_limiting, except: :commits_root
before_action :disable_query_limiting, except: :commits_root
before_action :require_non_empty_project
before_action :assign_ref_vars, except: :commits_root
before_action :authorize_download_code!
......@@ -83,7 +83,7 @@ class Projects::CommitsController < Projects::ApplicationController
@commits = set_commits_for_rendering(@commits)
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42330')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42330')
end
end
......@@ -7,7 +7,7 @@ class Projects::ForksController < Projects::ApplicationController
include Gitlab::Utils::StrongMemoize
# Authorize
before_action :whitelist_query_limiting, only: [:create]
before_action :disable_query_limiting, only: [:create]
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :authenticate_user!, only: [:new, :create]
......@@ -110,8 +110,8 @@ class Projects::ForksController < Projects::ApplicationController
access_denied! unless fork_namespace && fork_service.valid_fork_target?
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42335')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42335')
end
def load_namespaces_with_associations
......
......@@ -18,7 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController
prepend_before_action :authenticate_user!, only: [:new, :export_csv]
prepend_before_action :store_uri, only: [:new, :show, :designs]
before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update]
before_action :disable_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update]
before_action :check_issues_available!
before_action :issue, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
after_action :log_issue_show, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
......@@ -353,13 +353,13 @@ class Projects::IssuesController < Projects::ApplicationController
IssuesFinder
end
def whitelist_query_limiting
def disable_query_limiting
# Also see the following issues:
#
# 1. https://gitlab.com/gitlab-org/gitlab-foss/issues/42423
# 2. https://gitlab.com/gitlab-org/gitlab-foss/issues/42424
# 3. https://gitlab.com/gitlab-org/gitlab-foss/issues/42426
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42422')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42422')
end
private
......
......@@ -6,7 +6,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
include RendersCommits
skip_before_action :merge_request
before_action :whitelist_query_limiting, only: [:create]
before_action :disable_query_limiting, only: [:create]
before_action :authorize_create_merge_request_from!
before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path]
before_action :build_merge_request, except: [:create]
......@@ -133,8 +133,8 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
end
# rubocop: enable CodeReuse/ActiveRecord
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42384')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42384')
end
def incr_count_webide_merge_request
......
......@@ -14,7 +14,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
skip_before_action :merge_request, only: [:index, :bulk_update, :export_csv]
before_action :apply_diff_view_cookie!, only: [:show]
before_action :whitelist_query_limiting, only: [:assign_related_issues, :update]
before_action :disable_query_limiting, only: [:assign_related_issues, :update]
before_action :authorize_update_issuable!, only: [:close, :edit, :update, :remove_wip, :sort]
before_action :authorize_read_actual_head_pipeline!, only: [
:test_reports,
......@@ -468,9 +468,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
access_denied! unless @merge_request.can_be_merged_by?(current_user)
end
def whitelist_query_limiting
def disable_query_limiting
# Also see https://gitlab.com/gitlab-org/gitlab-foss/issues/42441
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42438')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42438')
end
def reports_response(report_comparison, pipeline = nil)
......
......@@ -4,7 +4,7 @@ class Projects::NetworkController < Projects::ApplicationController
include ExtractsPath
include ApplicationHelper
before_action :whitelist_query_limiting
before_action :disable_query_limiting
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_download_code!
......@@ -42,7 +42,7 @@ class Projects::NetworkController < Projects::ApplicationController
@commit = @repo.commit(@options[:extended_sha1])
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42333')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42333')
end
end
......@@ -6,7 +6,7 @@ class Projects::NotesController < Projects::ApplicationController
include NotesHelper
include ToggleAwardEmoji
before_action :whitelist_query_limiting, only: [:create, :update]
before_action :disable_query_limiting, only: [:create, :update]
before_action :authorize_read_note!
before_action :authorize_create_note!, only: [:create]
before_action :authorize_resolve_note!, only: [:resolve, :unresolve]
......@@ -87,7 +87,7 @@ class Projects::NotesController < Projects::ApplicationController
access_denied! unless can?(current_user, :create_note, noteable)
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42383')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42383')
end
end
......@@ -4,7 +4,7 @@ class Projects::PipelinesController < Projects::ApplicationController
include ::Gitlab::Utils::StrongMemoize
include Analytics::UniqueVisitsHelper
before_action :whitelist_query_limiting, only: [:create, :retry]
before_action :disable_query_limiting, only: [:create, :retry]
before_action :pipeline, except: [:index, :new, :create, :charts, :config_variables]
before_action :set_pipeline_path, only: [:show]
before_action :authorize_read_pipeline!
......@@ -92,7 +92,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def show
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/26657')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/26657')
respond_to do |format|
format.html
......@@ -269,9 +269,9 @@ class Projects::PipelinesController < Projects::ApplicationController
&.present(current_user: current_user)
end
def whitelist_query_limiting
def disable_query_limiting
# Also see https://gitlab.com/gitlab-org/gitlab-foss/issues/42343
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42339')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42339')
end
def authorize_update_pipeline!
......
......@@ -14,7 +14,7 @@ class ProjectsController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
before_action :whitelist_query_limiting, only: [:show, :create]
before_action :disable_query_limiting, only: [:show, :create]
before_action :authenticate_user!, except: [:index, :show, :activity, :refs, :resolve, :unfoldered_environment_names]
before_action :redirect_git_extension, only: [:show]
before_action :project, except: [:index, :new, :create, :resolve]
......@@ -510,8 +510,8 @@ class ProjectsController < Projects::ApplicationController
redirect_to(request.original_url.sub(%r{\.git/?\Z}, ''))
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/20826')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20826')
end
def present_project
......
......@@ -9,7 +9,7 @@ class RegistrationsController < Devise::RegistrationsController
layout 'devise'
prepend_before_action :check_captcha, only: :create
before_action :whitelist_query_limiting, :ensure_destroy_prerequisites_met, only: [:destroy]
before_action :disable_query_limiting, :ensure_destroy_prerequisites_met, only: [:destroy]
before_action :load_recaptcha, only: :new
before_action :set_invite_params, only: :new
......@@ -162,8 +162,8 @@ class RegistrationsController < Devise::RegistrationsController
@devise_mapping ||= Devise.mappings[:user]
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42380')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42380')
end
def load_recaptcha
......
......@@ -18,7 +18,7 @@ module Mutations
argument :api_url, GraphQL::STRING_TYPE,
required: true,
description: 'Endpoint at which prometheus can be queried.'
description: 'Endpoint at which Prometheus can be queried.'
def resolve(args)
project = authorized_find!(args[:project_path])
......
......@@ -16,7 +16,7 @@ module Mutations
argument :api_url, GraphQL::STRING_TYPE,
required: false,
description: "Endpoint at which prometheus can be queried."
description: "Endpoint at which Prometheus can be queried."
def resolve(args)
integration = authorized_find!(id: args[:id])
......
......@@ -53,7 +53,7 @@ module Mutations
end
def resolve(board:, **args)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/247861')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/247861')
raise_resource_not_available_error! unless board
authorize_board!(board)
......
......@@ -19,7 +19,7 @@ module Mutations
def resolve(project_path:, iid:, assignee_usernames:, operation_mode: Types::MutationOperationModeEnum.enum[:replace])
resource = authorized_find!(project_path: project_path, iid: iid)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/36098') if resource.is_a?(MergeRequest)
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/36098') if resource.is_a?(MergeRequest)
update_service_class.new(
resource.project,
......
......@@ -11,7 +11,7 @@ module Mutations
description: 'The project to move the issue to.'
def resolve(project_path:, iid:, target_project_path:)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/267762')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/267762')
issue = authorized_find!(project_path: project_path, iid: iid)
source_project = issue.project
......
......@@ -42,7 +42,8 @@ module Mutations
description: 'Squash commits on the source branch before merge.'
def resolve(project_path:, iid:, **args)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317')
merge_request = authorized_find!(project_path: project_path, iid: iid)
project = merge_request.target_project
merge_params = args.compact.with_indifferent_access
......
......@@ -8,15 +8,16 @@ module Resolvers
argument :path, GraphQL::STRING_TYPE,
required: true,
description: "Path to a file which defines metrics dashboard " \
"eg: 'config/prometheus/common_metrics.yml'."
description: <<~MD
Path to a file which defines a metrics dashboard eg: `"config/prometheus/common_metrics.yml"`.
MD
alias_method :environment, :object
def resolve(**args)
def resolve(path:)
return unless environment
::PerformanceMonitoring::PrometheusDashboard.find_for(**args, **service_params)
::PerformanceMonitoring::PrometheusDashboard.find_for(path: path, **service_params)
end
private
......
......@@ -7,7 +7,7 @@ module Resolvers
argument :ids, [GraphQL::ID_TYPE],
required: false,
description: 'Array of global milestone IDs, e.g., "gid://gitlab/Milestone/1".'
description: 'Array of global milestone IDs, e.g., `"gid://gitlab/Milestone/1"`.'
argument :state, Types::MilestoneStateEnum,
required: false,
......
......@@ -7,6 +7,7 @@ module Ci
include StripAttribute
include Schedulable
include Limitable
include EachBatch
self.limit_name = 'ci_pipeline_schedules'
self.limit_scope = :project
......@@ -28,6 +29,7 @@ module Ci
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) }
scope :preloaded, -> { preload(:owner, project: [:route]) }
scope :owned_by, ->(user) { where(owner: user) }
accepts_nested_attributes_for :variables, allow_destroy: true
......
......@@ -351,6 +351,7 @@ class User < ApplicationRecord
# For this reason the tradeoff is to disable this cop.
after_transition any => :blocked do |user|
Ci::CancelUserPipelinesService.new.execute(user)
Ci::DisableUserPipelineSchedulesService.new.execute(user)
end
# rubocop: enable CodeReuse/ServiceClass
end
......
# frozen_string_literal: true
module Ci
class DisableUserPipelineSchedulesService
def execute(user)
Ci::PipelineSchedule.active.owned_by(user).each_batch do |relation|
relation.update_all(active: false)
end
end
end
end
......@@ -48,7 +48,7 @@ class PostReceiveService
end
def process_mr_push_options(push_options, changes)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/61359')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/61359')
return unless repository
unless repository.repo_type.project?
......
---
title: Project Settings Operations header Grafana authentication expand/collapse on-click/on-tap
merge_request: 56270
author: Daniel Schömer
type: changed
---
title: Disable pipeline schedules when a user is blocked
merge_request: 56513
author:
type: changed
---
title: Add dast_profile_secret_variables table
merge_request: 56067
author:
type: added
# frozen_string_literal: true
if Gitlab::QueryLimiting.enable?
if Gitlab::QueryLimiting.enabled_for_env?
require_dependency 'gitlab/query_limiting/active_support_subscriber'
require_dependency 'gitlab/query_limiting/transaction'
require_dependency 'gitlab/query_limiting/middleware'
......
# frozen_string_literal: true
class CreateDastSiteProfileVariables < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
table_comment = { owner: 'group::dynamic analysis', description: 'Secret variables used in DAST on-demand scans' }
encrypted_value_constraint_name = check_constraint_name(:dast_site_profile_secret_variables, 'encrypted_value', 'max_length')
encrypted_value_iv_constraint_name = check_constraint_name(:dast_site_profile_secret_variables, 'encrypted_value_iv', 'max_length')
create_table_with_constraints :dast_site_profile_secret_variables, comment: table_comment.to_json do |t|
t.references :dast_site_profile, null: false, foreign_key: { on_delete: :cascade }, index: false
t.timestamps_with_timezone
t.integer :variable_type, null: false, default: 1, limit: 2
t.text :key, null: false
t.binary :encrypted_value, null: false
t.binary :encrypted_value_iv, null: false, unique: true
t.index [:dast_site_profile_id, :key], unique: true, name: :index_site_profile_secret_variables_on_site_profile_id_and_key
t.text_limit :key, 255
# This does not currently have first-class support via create_table_with_constraints
t.check_constraint encrypted_value_constraint_name, 'length(encrypted_value) <= 13352'
t.check_constraint encrypted_value_iv_constraint_name, 'length(encrypted_value_iv) <= 17'
end
end
def down
drop_table :dast_site_profile_secret_variables
end
end
# frozen_string_literal: true
class AddOwnerAndIdIndexOnActiveCiPipelineSchedules < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_ci_pipeline_schedules_on_owner_id_and_id_and_active'
disable_ddl_transaction!
def up
add_concurrent_index :ci_pipeline_schedules, [:owner_id, :id], where: "active = TRUE", name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :ci_pipeline_schedules, INDEX_NAME
end
end
d91eb442db670adef6d610a2c79259377709e5c98615ba10b85eb998715b3130
\ No newline at end of file
5d63a48f4a9327f683eff093d2862a0b88aa4249c94b2de9751ed6172c9b4799
\ No newline at end of file
......@@ -11808,6 +11808,31 @@ CREATE SEQUENCE dast_scanner_profiles_id_seq
ALTER SEQUENCE dast_scanner_profiles_id_seq OWNED BY dast_scanner_profiles.id;
CREATE TABLE dast_site_profile_secret_variables (
id bigint NOT NULL,
dast_site_profile_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
variable_type smallint DEFAULT 1 NOT NULL,
key text NOT NULL,
encrypted_value bytea NOT NULL,
encrypted_value_iv bytea NOT NULL,
CONSTRAINT check_236213f179 CHECK ((length(encrypted_value) <= 13352)),
CONSTRAINT check_8cbef204b2 CHECK ((char_length(key) <= 255)),
CONSTRAINT check_b49080abbf CHECK ((length(encrypted_value_iv) <= 17))
);
COMMENT ON TABLE dast_site_profile_secret_variables IS '{"owner":"group::dynamic analysis","description":"Secret variables used in DAST on-demand scans"}';
CREATE SEQUENCE dast_site_profile_secret_variables_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE dast_site_profile_secret_variables_id_seq OWNED BY dast_site_profile_secret_variables.id;
CREATE TABLE dast_site_profiles (
id bigint NOT NULL,
project_id bigint NOT NULL,
......@@ -19141,6 +19166,8 @@ ALTER TABLE ONLY dast_profiles ALTER COLUMN id SET DEFAULT nextval('dast_profile
ALTER TABLE ONLY dast_scanner_profiles ALTER COLUMN id SET DEFAULT nextval('dast_scanner_profiles_id_seq'::regclass);
ALTER TABLE ONLY dast_site_profile_secret_variables ALTER COLUMN id SET DEFAULT nextval('dast_site_profile_secret_variables_id_seq'::regclass);
ALTER TABLE ONLY dast_site_profiles ALTER COLUMN id SET DEFAULT nextval('dast_site_profiles_id_seq'::regclass);
ALTER TABLE ONLY dast_site_tokens ALTER COLUMN id SET DEFAULT nextval('dast_site_tokens_id_seq'::regclass);
......@@ -20341,6 +20368,9 @@ ALTER TABLE ONLY dast_profiles
ALTER TABLE ONLY dast_scanner_profiles
ADD CONSTRAINT dast_scanner_profiles_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dast_site_profile_secret_variables
ADD CONSTRAINT dast_site_profile_secret_variables_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dast_site_profiles
ADD CONSTRAINT dast_site_profiles_pkey PRIMARY KEY (id);
......@@ -22015,6 +22045,8 @@ CREATE INDEX index_ci_pipeline_schedules_on_next_run_at_and_active ON ci_pipelin
CREATE INDEX index_ci_pipeline_schedules_on_owner_id ON ci_pipeline_schedules USING btree (owner_id);
CREATE INDEX index_ci_pipeline_schedules_on_owner_id_and_id_and_active ON ci_pipeline_schedules USING btree (owner_id, id) WHERE (active = true);
CREATE INDEX index_ci_pipeline_schedules_on_project_id ON ci_pipeline_schedules USING btree (project_id);
CREATE UNIQUE INDEX index_ci_pipeline_variables_on_pipeline_id_and_key ON ci_pipeline_variables USING btree (pipeline_id, key);
......@@ -23679,6 +23711,8 @@ CREATE UNIQUE INDEX index_services_on_unique_group_id_and_type ON services USING
CREATE UNIQUE INDEX index_shards_on_name ON shards USING btree (name);
CREATE UNIQUE INDEX index_site_profile_secret_variables_on_site_profile_id_and_key ON dast_site_profile_secret_variables USING btree (dast_site_profile_id, key);
CREATE INDEX index_slack_integrations_on_service_id ON slack_integrations USING btree (service_id);
CREATE UNIQUE INDEX index_slack_integrations_on_team_id_and_alias ON slack_integrations USING btree (team_id, alias);
......@@ -25560,6 +25594,9 @@ ALTER TABLE ONLY operations_strategies_user_lists
ALTER TABLE ONLY lfs_file_locks
ADD CONSTRAINT fk_rails_43df7a0412 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY dast_site_profile_secret_variables
ADD CONSTRAINT fk_rails_43e2897950 FOREIGN KEY (dast_site_profile_id) REFERENCES dast_site_profiles(id) ON DELETE CASCADE;
ALTER TABLE ONLY merge_request_assignees
ADD CONSTRAINT fk_rails_443443ce6f FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
......@@ -15,30 +15,30 @@ When a test fails because it executes more than 100 SQL queries there are two
solutions to this problem:
- Reduce the number of SQL queries that are executed.
- Whitelist the controller or API endpoint.
- Disable query limiting for the controller or API endpoint.
You should only resort to whitelisting when an existing controller or endpoint
You should only resort to disabling query limits when an existing controller or endpoint
is to blame as in this case reducing the number of SQL queries can take a lot of
effort. Newly added controllers and endpoints are not allowed to execute more
than 100 SQL queries and no exceptions are made for this rule. _If_ a large
number of SQL queries is necessary to perform certain work it's best to have
this work performed by Sidekiq instead of doing this directly in a web request.
## Whitelisting
## Disable query limiting
In the event that you _have_ to whitelist a controller you must first
In the event that you _have_ to disable query limits for a controller, you must first
create an issue. This issue should (preferably in the title) mention the
controller or endpoint and include the appropriate labels (`database`,
`performance`, and at least a team specific label such as `Discussion`).
After the issue has been created you can whitelist the code in question. For
After the issue has been created, you can disable query limits on the code in question. For
Rails controllers it's best to create a `before_action` hook that runs as early
as possible. The called method in turn should call
`Gitlab::QueryLimiting.whitelist('issue URL here')`. For example:
`Gitlab::QueryLimiting.disable!('issue URL here')`. For example:
```ruby
class MyController < ApplicationController
before_action :whitelist_query_limiting, only: [:show]
before_action :disable_query_limiting, only: [:show]
def index
# ...
......@@ -48,8 +48,8 @@ class MyController < ApplicationController
# ...
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/...')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/...')
end
end
```
......@@ -63,7 +63,7 @@ call directly into the endpoint like so:
```ruby
get '/projects/:id/foo' do
Gitlab::QueryLimiting.whitelist('...')
Gitlab::QueryLimiting.disable!('...')
# ...
end
......
---
stage: Plan
group: Product Planning
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Epic Boards **(ULTIMATE)**
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2864) in GitLab 13.10.
> - It's [deployed behind a feature flag](../../feature_flags.md), disabled by default.
> - It's disabled on GitLab.com.
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](../../../administration/feature_flags.md).
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
The GitLab Epic Board is a software project management tool used to plan,
organize, and visualize a workflow for a feature or product release.
Epic boards build on the existing [epic tracking functionality](index.md) and
[labels](../../project/labels.md). Your epics appear as cards in vertical lists, organized by their assigned
labels.
To view an epic board, in a group, select **Epics > Boards**.
![GitLab epic board - Ultimate](img/epic_board_v13_10.png)
## Create an epic board
To create a new epic board:
1. Select the dropdown with the current board name in the upper left corner of the Epic Boards page.
1. Select **Create new board**.
1. Enter the new board's name and select **Create**.
## Limitations of epic boards
As of GitLab 13.10, these limitations apply:
- Epic Boards need to be enabled by an administrator.
- Epic Boards can be created but not deleted.
- Lists can be added to the board but not deleted.
- There is no sidebar on the board. To edit an epic, go to the epic's page.
- There is no drag and drop support yet. To move an epic between lists, edit epic labels on the epic's page.
- Epics cannot be re-ordered within the list.
To learn more about the future iterations of this feature, visit
[epic 5067](https://gitlab.com/groups/gitlab-org/-/epics/5067).
## Enable or disable Epic Boards
Epic Boards are under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can enable it.
To enable it:
```ruby
Feature.enable(:epic_boards)
```
To disable it:
```ruby
Feature.disable(:epic_boards)
```
......@@ -9,8 +9,7 @@ module EE
prepended do
include DescriptionDiffActions
before_action :whitelist_query_limiting_ee, only: [:update]
before_action :disable_query_limiting_ee, only: [:update]
before_action only: [:new, :create] do
populate_vulnerability_id
end
......@@ -43,8 +42,8 @@ module EE
options.reject { |key| key == 'weight' }
end
def whitelist_query_limiting_ee
::Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/4794')
def disable_query_limiting_ee
::Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/4794')
end
def issue_params
......
......@@ -16,7 +16,7 @@ module EE
push_frontend_feature_flag(:usage_data_i_testing_load_performance_widget_total, @project, default_enabled: true)
end
before_action :whitelist_query_limiting_ee_merge, only: [:merge]
before_action :disable_query_limiting_ee_merge, only: [:merge]
before_action :authorize_read_pipeline!, only: [:container_scanning_reports, :dependency_scanning_reports,
:sast_reports, :secret_detection_reports, :dast_reports,
:metrics_reports, :coverage_fuzzing_reports,
......@@ -64,8 +64,8 @@ module EE
private
def whitelist_query_limiting_ee_merge
::Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/4792')
def disable_query_limiting_ee_merge
::Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/4792')
end
end
end
......
......@@ -8,6 +8,8 @@ module Dast
belongs_to :dast_site_profile
belongs_to :dast_scanner_profile
has_many :secret_variables, through: :dast_site_profile, class_name: 'Dast::SiteProfileSecretVariable'
validates :description, length: { maximum: 255 }
validates :name, length: { maximum: 255 }, uniqueness: { scope: :project_id }, presence: true
validates :branch_name, length: { maximum: 255 }
......
# frozen_string_literal: true
module Dast
class SiteProfileSecretVariable < ApplicationRecord
include Ci::HasVariable
include Ci::Maskable
self.table_name = 'dast_site_profile_secret_variables'
belongs_to :dast_site_profile
delegate :project, to: :dast_site_profile, allow_nil: false
attribute :masked, default: true
attr_encrypted :value,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_32,
encode: false # No need to encode for binary column https://github.com/attr-encrypted/attr_encrypted#the-encode-encode_iv-encode_salt-and-default_encoding-options
# Secret variables must be masked to prevent them being readable in CI jobs
validates :masked, inclusion: { in: [true] }
validates :variable_type, inclusion: { in: ['env_var'] }
validates :key, uniqueness: { scope: :dast_site_profile_id, message: "(%{value}) has already been taken" }
# Since user input is base64 encoded before being encrypted, we must validate against the encoded length
MAX_VALUE_LENGTH = 10_000
MAX_ENCODED_VALUE_LENGTH = ((4 * MAX_VALUE_LENGTH / 3) + 3) & ~3
validates :value, length: {
maximum: MAX_ENCODED_VALUE_LENGTH, # encoded user input length
too_long: -> (object, data) { "exceeds the #{MAX_VALUE_LENGTH} character limit" } # user input length
}
# User input is base64 encoded before being encrypted in order to allow it to be masked by default
def raw_value=(new_value)
self.value = Base64.strict_encode64(new_value)
end
# Use #raw_value= to ensure value is maskable
private :value=
end
end
......@@ -4,6 +4,8 @@ class DastSiteProfile < ApplicationRecord
belongs_to :project
belongs_to :dast_site
has_many :secret_variables, class_name: 'Dast::SiteProfileSecretVariable'
validates :name, length: { maximum: 255 }, uniqueness: { scope: :project_id }, presence: true
validates :project_id, :dast_site_id, presence: true
validate :dast_site_project_id_fk
......
......@@ -115,7 +115,7 @@ module API
at_least_one_of :title, :description, :start_date_fixed, :start_date_is_fixed, :due_date_fixed, :due_date_is_fixed, :labels, :add_labels, :remove_labels, :state_event, :confidential
end
put ':id/(-/)epics/:epic_iid' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/194104')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/194104')
authorize_can_admin_epic!
......
......@@ -90,7 +90,7 @@ module EE
desc: 'Array of Group IDs to set as approvers.'
end
put 'approvers' do
::Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/8883')
::Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/8883')
merge_request = find_merge_request_with_access(params[:merge_request_iid], :update_approvers)
......
# frozen_string_literal: true
FactoryBot.define do
factory :dast_site_profile_secret_variable, class: 'Dast::SiteProfileSecretVariable' do
dast_site_profile
sequence(:key) { |n| "VARIABLE_#{n}" }
raw_value { 'VARIABLE_VALUE' }
end
end
......@@ -9,6 +9,7 @@ RSpec.describe Dast::Profile, type: :model do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:dast_site_profile) }
it { is_expected.to belong_to(:dast_scanner_profile) }
it { is_expected.to have_many(:secret_variables).through(:dast_site_profile).class_name('Dast::SiteProfileSecretVariable') }
end
describe 'validations' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Dast::SiteProfileSecretVariable, type: :model do
let_it_be(:dast_site_profile) { create(:dast_site_profile) }
subject { create(:dast_site_profile_secret_variable, dast_site_profile: dast_site_profile) }
it_behaves_like 'CI variable'
describe 'constants' do
describe 'MAX_ENCODED_VALUE_LENGTH' do
it 'correctly expresses the relationship between input and encoded length' do
raw_value = SecureRandom.alphanumeric(described_class::MAX_VALUE_LENGTH)
expect(described_class::MAX_ENCODED_VALUE_LENGTH).to eq(Base64.strict_encode64(raw_value).length)
end
end
end
describe 'associations' do
it { is_expected.to belong_to(:dast_site_profile) }
end
describe 'validations' do
it { is_expected.to be_valid }
it { is_expected.to include_module(Ci::Maskable) }
it { is_expected.to include_module(Ci::HasVariable) }
it { is_expected.to validate_inclusion_of(:masked).in_array([true]) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:dast_site_profile_id).with_message(/\(\w+\) has already been taken/) }
it 'only allows records where variable_type=env_var', :aggregate_failures do
subject = build(:dast_site_profile_secret_variable, variable_type: :file)
expect(subject).not_to be_valid
expect(subject.errors.full_messages).to include('Variable type is not included in the list')
end
describe '#value' do
subject { build(:dast_site_profile_secret_variable, dast_site_profile: dast_site_profile, raw_value: raw_value) }
context 'when the value is over the limit' do
let(:raw_value) { SecureRandom.alphanumeric(10_003) }
it 'is not valid', :aggregate_failures do
expect(subject).not_to be_valid
expect(subject.errors.full_messages).to include('Value exceeds the 10000 character limit')
end
it 'raises a database level error' do
allow(subject).to receive(:valid?).and_return(true)
expect { subject.save! }.to raise_error(ActiveRecord::StatementInvalid)
end
end
context 'when value is under the limit' do
let(:raw_value) { SecureRandom.alphanumeric(10_000) }
it 'is valid' do
expect(subject).to be_valid
end
it 'does not raise database level error' do
allow(subject).to receive(:valid?).and_return(true)
expect { subject.save! }.not_to raise_error
end
end
end
end
describe '#masked' do
it 'defaults to true', :aggregate_failures do
expect(subject.masked).to eq(true)
expect(described_class.new.masked).to eq(true)
end
end
describe '#project' do
it 'delegates to dast_site_profile' do
expect(subject.project).to eq(subject.dast_site_profile.project)
end
end
describe '#raw_value=' do
it 'pre-encodes the value' do
value = SecureRandom.alphanumeric
subject = create(:dast_site_profile_secret_variable, raw_value: value)
expect(Base64.strict_decode64(subject.value)).to eq(value)
end
end
describe '#value=' do
it 'raises an error because #raw_value= should be used instead' do
expect { subject.value = SecureRandom.alphanumeric }.to raise_error(NoMethodError, /private method `value=' called for/)
end
end
describe '#variable_type' do
it 'defaults to env_var', :aggregate_failures do
variable_type = 'env_var'
expect(subject.variable_type).to eq(variable_type)
expect(described_class.new.variable_type).to eq(variable_type)
end
end
end
......@@ -8,6 +8,7 @@ RSpec.describe DastSiteProfile, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:dast_site) }
it { is_expected.to have_many(:secret_variables).class_name('Dast::SiteProfileSecretVariable') }
end
describe 'validations' do
......
......@@ -70,7 +70,7 @@ module API
optional :variables, Array, desc: 'Array of variables available in the pipeline'
end
post ':id/pipeline' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42124')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42124')
authorize! :create_pipeline, user_project
......
......@@ -112,7 +112,7 @@ module API
end
def delete_group(group)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/46285')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/46285')
destroy_conditionally!(group) do |group|
::Groups::DestroyService.new(group, current_user).async_execute
end
......
......@@ -116,7 +116,7 @@ module API
end
def create_note(noteable, opts)
whitelist_query_limiting
disable_query_limiting
authorize!(:create_note, noteable)
parent = noteable_parent(noteable)
......@@ -144,8 +144,8 @@ module API
present discussion, with: Entities::Discussion
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/211538')
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/211538')
end
end
end
......
......@@ -242,7 +242,7 @@ module API
use :issue_params
end
post ':id/issues' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42320')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42320')
check_rate_limit! :issues_create, [current_user]
......@@ -288,7 +288,7 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
put ':id/issues/:issue_iid' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42322')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42322')
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue
......@@ -346,7 +346,7 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
post ':id/issues/:issue_iid/move' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42323')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42323')
issue = user_project.issues.find_by(iid: params[:issue_iid])
not_found!('Issue') unless issue
......
......@@ -207,7 +207,7 @@ module API
use :optional_params
end
post ":id/merge_requests" do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42316')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42316')
authorize! :create_merge_request_from, user_project
......@@ -416,7 +416,7 @@ module API
at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of)
end
put ':id/merge_requests/:merge_request_iid' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42318')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42318')
merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request)
......@@ -445,7 +445,7 @@ module API
optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
end
put ':id/merge_requests/:merge_request_iid/merge' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317')
merge_request = find_project_merge_request(params[:merge_request_iid])
......
......@@ -67,7 +67,7 @@ module API
check_rate_limit! :project_import, [current_user, :project_import]
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
validate_file!
......
......@@ -215,7 +215,7 @@ module API
use :create_params
end
post do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/21139')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/21139')
attrs = declared_params(include_missing: false)
attrs = translate_params_for_compatibility(attrs)
filter_attributes_using_license!(attrs)
......@@ -248,7 +248,7 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
post "user/:user_id", feature_category: :projects do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/21139')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/21139')
authenticated_as_admin!
user = User.find_by(id: params.delete(:user_id))
not_found!('User') unless user
......@@ -310,7 +310,7 @@ module API
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the fork'
end
post ':id/fork', feature_category: :source_code_management do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42284')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42284')
not_found! unless can?(current_user, :fork_project, user_project)
......
......@@ -21,7 +21,7 @@ module API
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end
post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42283')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42283')
forbidden! if gitlab_pipeline_hook_request?
......
......@@ -571,7 +571,7 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
delete ":id", feature_category: :users do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/20757')
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/20757')
authenticated_as_admin!
......
......@@ -6,28 +6,36 @@ module Gitlab
#
# This is only enabled in development and test to ensure we don't produce
# any errors that users of other environments can't do anything about themselves.
def self.enable?
def self.enabled_for_env?
Rails.env.development? || Rails.env.test?
end
def self.enabled?
enabled_for_env? &&
!Gitlab::SafeRequestStore[:query_limiting_disabled]
end
# Allows the current request to execute any number of SQL queries.
#
# This method should _only_ be used when there's a corresponding issue to
# reduce the number of queries.
#
# The issue URL is only meant to push developers into creating an issue
# instead of blindly whitelisting offending blocks of code.
def self.whitelist(issue_url)
return unless enable?
# instead of blindly disabling for offending blocks of code.
def self.disable!(issue_url)
unless issue_url.start_with?('https://')
raise(
ArgumentError,
'You must provide a valid issue URL in order to whitelist a block of code'
'You must provide a valid issue URL in order to allow a block of code'
)
end
Transaction&.current&.whitelisted = true
Gitlab::SafeRequestStore[:query_limiting_disabled] = true
end
# Enables query limiting for the request.
def self.enable!
Gitlab::SafeRequestStore[:query_limiting_disabled] = nil
end
end
end
......@@ -5,7 +5,7 @@ module Gitlab
class Transaction
THREAD_KEY = :__gitlab_query_counts_transaction
attr_accessor :count, :whitelisted
attr_accessor :count
# The name of the action (e.g. `UsersController#show`) that is being
# executed.
......@@ -45,7 +45,6 @@ module Gitlab
def initialize
@action = nil
@count = 0
@whitelisted = false
@sql_executed = []
end
......@@ -59,7 +58,7 @@ module Gitlab
end
def increment
@count += 1 unless whitelisted
@count += 1 if enabled?
end
def executed_sql(sql)
......@@ -83,6 +82,10 @@ module Gitlab
["#{header}: #{msg}", log, ellipsis].compact.join("\n")
end
def enabled?
::Gitlab::QueryLimiting.enabled?
end
end
end
end
......@@ -709,9 +709,9 @@ RSpec.describe 'Pipeline', :js do
end
end
it 'displays the PipelineSchedule in an active state' do
it 'displays the PipelineSchedule in an inactive state' do
visit project_pipeline_schedules_path(project)
page.click_link('Active')
page.click_link('Inactive')
expect(page).to have_selector('table.ci-table > tbody > tr > td', text: 'blocked user schedule')
end
......
......@@ -9,7 +9,7 @@ exports[`grafana integration component default state to match the default snapsh
class="settings-header"
>
<h4
class="js-section-header"
class="js-section-header settings-title js-settings-toggle js-settings-toggle-trigger-only"
>
Grafana authentication
......
import Vue from 'vue';
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { trimText } from 'helpers/text_helper';
import mountComponent from 'helpers/vue_mount_component_helper';
import component from '~/jobs/components/stages_dropdown.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import StagesDropdown from '~/jobs/components/stages_dropdown.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import {
mockPipelineWithoutMR,
mockPipelineWithAttachedMR,
mockPipelineDetached,
} from '../mock_data';
describe('Stages Dropdown', () => {
const Component = Vue.extend(component);
let vm;
const mockPipelineData = {
id: 28029444,
details: {
status: {
details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
},
path: 'pipeline/28029444',
flags: {
merge_request_pipeline: true,
detached_merge_request_pipeline: false,
},
merge_request: {
iid: 1234,
path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
title: 'Update README.md',
source_branch: 'feature-1234',
source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
target_branch: 'master',
target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
},
ref: {
name: 'test-branch',
let wrapper;
const findStatus = () => wrapper.findComponent(CiIcon);
const findSelectedStageText = () => wrapper.findComponent(GlDropdown).props('text');
const findStageItem = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
const findPipelineInfoText = () => wrapper.findByTestId('pipeline-info').text();
const findPipelinePath = () => wrapper.findByTestId('pipeline-path').attributes('href');
const findMRLinkPath = () => wrapper.findByTestId('mr-link').attributes('href');
const findSourceBranchLinkPath = () =>
wrapper.findByTestId('source-branch-link').attributes('href');
const findTargetBranchLinkPath = () =>
wrapper.findByTestId('target-branch-link').attributes('href');
const createComponent = (props) => {
wrapper = extendedWrapper(
shallowMount(StagesDropdown, {
propsData: {
...props,
},
}),
);
};
describe('without a merge request pipeline', () => {
let pipeline;
afterEach(() => {
wrapper.destroy();
});
describe('without a merge request pipeline', () => {
beforeEach(() => {
pipeline = JSON.parse(JSON.stringify(mockPipelineData));
delete pipeline.merge_request;
delete pipeline.flags.merge_request_pipeline;
delete pipeline.flags.detached_merge_request_pipeline;
vm = mountComponent(Component, {
pipeline,
createComponent({
pipeline: mockPipelineWithoutMR,
stages: [{ name: 'build' }, { name: 'test' }],
selectedStage: 'deploy',
});
});
afterEach(() => {
vm.$destroy();
});
it('renders pipeline status', () => {
expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull();
expect(findStatus().exists()).toBe(true);
});
it('renders pipeline link', () => {
expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual(
'pipeline/28029444',
);
expect(findPipelinePath()).toBe('pipeline/28029444');
});
it('renders dropdown with stages', () => {
expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build');
expect(findStageItem(0).text()).toBe('build');
});
it('rendes selected stage', () => {
expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy');
expect(findSelectedStageText()).toBe('deploy');
});
it(`renders the pipeline info text like "Pipeline #123 for source_branch"`, () => {
const expected = `Pipeline #${pipeline.id} for ${pipeline.ref.name}`;
const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
const expected = `Pipeline #${mockPipelineWithoutMR.id} for ${mockPipelineWithoutMR.ref.name}`;
const actual = trimText(findPipelineInfoText());
expect(actual).toBe(expected);
});
});
describe('with an "attached" merge request pipeline', () => {
let pipeline;
beforeEach(() => {
pipeline = JSON.parse(JSON.stringify(mockPipelineData));
pipeline.flags.merge_request_pipeline = true;
pipeline.flags.detached_merge_request_pipeline = false;
vm = mountComponent(Component, {
pipeline,
createComponent({
pipeline: mockPipelineWithAttachedMR,
stages: [],
selectedStage: 'deploy',
});
});
it(`renders the pipeline info text like "Pipeline #123 for !456 with source_branch into target_branch"`, () => {
const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch} into ${pipeline.merge_request.target_branch}`;
const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
const expected = `Pipeline #${mockPipelineWithAttachedMR.id} for !${mockPipelineWithAttachedMR.merge_request.iid} with ${mockPipelineWithAttachedMR.merge_request.source_branch} into ${mockPipelineWithAttachedMR.merge_request.target_branch}`;
const actual = trimText(findPipelineInfoText());
expect(actual).toBe(expected);
});
it(`renders the correct merge request link`, () => {
const actual = vm.$el.querySelector('.js-mr-link').href;
expect(actual).toContain(pipeline.merge_request.path);
expect(findMRLinkPath()).toBe(mockPipelineWithAttachedMR.merge_request.path);
});
it(`renders the correct source branch link`, () => {
const actual = vm.$el.querySelector('.js-source-branch-link').href;
expect(actual).toContain(pipeline.merge_request.source_branch_path);
expect(findSourceBranchLinkPath()).toBe(
mockPipelineWithAttachedMR.merge_request.source_branch_path,
);
});
it(`renders the correct target branch link`, () => {
const actual = vm.$el.querySelector('.js-target-branch-link').href;
expect(actual).toContain(pipeline.merge_request.target_branch_path);
expect(findTargetBranchLinkPath()).toBe(
mockPipelineWithAttachedMR.merge_request.target_branch_path,
);
});
});
describe('with a detached merge request pipeline', () => {
let pipeline;
beforeEach(() => {
pipeline = JSON.parse(JSON.stringify(mockPipelineData));
pipeline.flags.merge_request_pipeline = false;
pipeline.flags.detached_merge_request_pipeline = true;
vm = mountComponent(Component, {
pipeline,
createComponent({
pipeline: mockPipelineDetached,
stages: [],
selectedStage: 'deploy',
});
});
it(`renders the pipeline info like "Pipeline #123 for !456 with source_branch"`, () => {
const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch}`;
const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
const expected = `Pipeline #${mockPipelineDetached.id} for !${mockPipelineDetached.merge_request.iid} with ${mockPipelineDetached.merge_request.source_branch}`;
const actual = trimText(findPipelineInfoText());
expect(actual).toBe(expected);
});
it(`renders the correct merge request link`, () => {
const actual = vm.$el.querySelector('.js-mr-link').href;
expect(actual).toContain(pipeline.merge_request.path);
expect(findMRLinkPath()).toBe(mockPipelineDetached.merge_request.path);
});
it(`renders the correct source branch link`, () => {
const actual = vm.$el.querySelector('.js-source-branch-link').href;
expect(actual).toContain(pipeline.merge_request.source_branch_path);
expect(findSourceBranchLinkPath()).toBe(
mockPipelineDetached.merge_request.source_branch_path,
);
});
});
});
......@@ -1189,3 +1189,86 @@ export const jobsInStage = {
path: '/gitlab-org/gitlab-shell/pipelines/27#build',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build',
};
export const mockPipelineWithoutMR = {
id: 28029444,
details: {
status: {
details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
},
path: 'pipeline/28029444',
ref: {
name: 'test-branch',
},
};
export const mockPipelineWithAttachedMR = {
id: 28029444,
details: {
status: {
details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
},
path: 'pipeline/28029444',
flags: {
merge_request_pipeline: true,
detached_merge_request_pipeline: false,
},
merge_request: {
iid: 1234,
path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
title: 'Update README.md',
source_branch: 'feature-1234',
source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
target_branch: 'master',
target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
},
ref: {
name: 'test-branch',
},
};
export const mockPipelineDetached = {
id: 28029444,
details: {
status: {
details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
},
path: 'pipeline/28029444',
flags: {
merge_request_pipeline: false,
detached_merge_request_pipeline: true,
},
merge_request: {
iid: 1234,
path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
title: 'Update README.md',
source_branch: 'feature-1234',
source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
target_branch: 'master',
target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
},
ref: {
name: 'test-branch',
},
};
......@@ -68,11 +68,15 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do
it 'increments the number of executed queries' do
transaction = described_class.new
expect(transaction.count).to be_zero
expect { transaction.increment }.to change { transaction.count }.by(1)
end
it 'does not increment the number of executed queries when query limiting is disabled' do
transaction = described_class.new
transaction.increment
allow(transaction).to receive(:enabled?).and_return(false)
expect(transaction.count).to eq(1)
expect { transaction.increment }.not_to change { transaction.count }
end
end
......
......@@ -2,81 +2,85 @@
require 'spec_helper'
RSpec.describe Gitlab::QueryLimiting do
describe '.enable?' do
RSpec.describe Gitlab::QueryLimiting, :request_store do
describe '.enabled_for_env?' do
it 'returns true in a test environment' do
expect(described_class.enable?).to eq(true)
expect(described_class.enabled_for_env?).to eq(true)
end
it 'returns true in a development environment' do
stub_rails_env('development')
stub_rails_env('development')
expect(described_class.enable?).to eq(true)
expect(described_class.enabled_for_env?).to eq(true)
end
it 'returns false on GitLab.com' do
stub_rails_env('production')
allow(Gitlab).to receive(:com?).and_return(true)
expect(described_class.enable?).to eq(false)
expect(described_class.enabled_for_env?).to eq(false)
end
it 'returns false in a non GitLab.com' do
allow(Gitlab).to receive(:com?).and_return(false)
stub_rails_env('production')
expect(described_class.enable?).to eq(false)
expect(described_class.enabled_for_env?).to eq(false)
end
end
describe '.whitelist' do
it 'raises ArgumentError when an invalid issue URL is given' do
expect { described_class.whitelist('foo') }
.to raise_error(ArgumentError)
end
context 'without a transaction' do
it 'does nothing' do
expect { described_class.whitelist('https://example.com') }
.not_to raise_error
shared_context 'disable and enable' do |result|
let(:transaction) { Gitlab::QueryLimiting::Transaction.new }
let(:code) do
proc do
2.times { User.count }
end
end
context 'with a transaction' do
let(:transaction) { Gitlab::QueryLimiting::Transaction.new }
before do
allow(Gitlab::QueryLimiting::Transaction)
.to receive(:current)
.and_return(transaction)
end
end
it 'does not increment the number of SQL queries executed in the block' do
before = transaction.count
described_class.whitelist('https://example.com')
describe '.disable!' do
include_context 'disable and enable'
2.times do
User.count
it 'raises an ArgumentError when an invalid issue URL is given' do
expect { described_class.disable!('foo') }
.to raise_error(ArgumentError)
end
expect(transaction.count).to eq(before)
it 'stops the number of SQL queries from being incremented' do
described_class.disable!('https://example.com')
expect { code.call }.not_to change { transaction.count }
end
end
it 'whitelists when enabled' do
described_class.whitelist('https://example.com')
describe '.enable!' do
include_context 'disable and enable'
expect(transaction.whitelisted).to eq(true)
end
it 'allows the number of SQL queries to be incremented' do
described_class.enable!
it 'does not whitelist when disabled' do
allow(described_class).to receive(:enable?).and_return(false)
expect { code.call }.to change { transaction.count }.by(2)
end
end
described_class.whitelist('https://example.com')
describe '#enabled?' do
it 'returns true when enabled' do
Gitlab::SafeRequestStore[:query_limiting_disabled] = nil
expect(transaction.whitelisted).to eq(false)
expect(described_class).to be_enabled
end
it 'returns false when disabled' do
Gitlab::SafeRequestStore[:query_limiting_disabled] = true
expect(described_class).not_to be_enabled
end
end
end
......@@ -90,6 +90,18 @@ RSpec.describe Ci::PipelineSchedule do
end
end
describe '.owned_by' do
let(:user) { create(:user) }
let!(:owned_pipeline_schedule) { create(:ci_pipeline_schedule, owner: user) }
let!(:other_pipeline_schedule) { create(:ci_pipeline_schedule) }
subject { described_class.owned_by(user) }
it 'returns owned pipeline schedules' do
is_expected.to eq([owned_pipeline_schedule])
end
end
describe '#set_next_run_at' do
let(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly) }
let(:ideal_next_run_at) { pipeline_schedule.send(:ideal_next_run_from, Time.zone.now) }
......
......@@ -1766,7 +1766,7 @@ RSpec.describe User do
end
describe 'blocking user' do
let(:user) { create(:user, name: 'John Smith') }
let_it_be_with_refind(:user) { create(:user, name: 'John Smith') }
it 'blocks user' do
user.block
......@@ -1789,6 +1789,14 @@ RSpec.describe User do
user.block
end
end
context 'when user has active CI pipeline schedules' do
let_it_be(:schedule) { create(:ci_pipeline_schedule, active: true, owner: user) }
it 'disables any pipeline schedules' do
expect { user.block }.to change { schedule.reload.active? }.to(false)
end
end
end
describe 'deactivating a user' do
......
......@@ -47,10 +47,10 @@ RSpec.describe 'getting merge request listings nested in a project' do
end
before do
# We cannot call the whitelist here, since the transaction does not
# We cannot disable SQL query limiting here, since the transaction does not
# begin until we enter the controller.
headers = {
'X-GITLAB-QUERY-WHITELIST-ISSUE' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/322979'
'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/322979'
}
post_graphql(query, current_user: current_user, headers: headers)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::DisableUserPipelineSchedulesService do
describe '#execute' do
let(:user) { create(:user) }
subject(:service) { described_class.new.execute(user) }
context 'when user has active pipeline schedules' do
let(:owned_pipeline_schedule) { create(:ci_pipeline_schedule, active: true, owner: user) }
it 'disables all active pipeline schedules', :aggregate_failures do
expect { service }.to change { owned_pipeline_schedule.reload.active? }
end
end
end
end
......@@ -359,6 +359,9 @@ RSpec.configure do |config|
# Reset all feature flag stubs to default for testing
stub_all_feature_flags
# Re-enable query limiting in case it was disabled
Gitlab::QueryLimiting.enable!
end
config.before(:example, :mailer) do
......
......@@ -15,20 +15,14 @@ end
# If Sidekiq::Testing.inline! is used, SQL transactions done inside
# Sidekiq worker are included in the SQL query limit (in a real
# deployment sidekiq worker is executed separately). To avoid
# increasing SQL limit counter, the request is marked as whitelisted
# during Sidekiq block
# deployment sidekiq worker is executed separately). To avoid increasing
# SQL limit counter, query limiting is disabled during Sidekiq block
class DisableQueryLimit
def call(worker_instance, msg, queue)
transaction = Gitlab::QueryLimiting::Transaction.current
if !transaction.respond_to?(:whitelisted) || transaction.whitelisted
yield
else
transaction.whitelisted = true
::Gitlab::QueryLimiting.disable!('https://mock-issue')
yield
transaction.whitelisted = false
end
ensure
::Gitlab::QueryLimiting.enable!
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment