Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-09-17 15:11:44 +00:00
parent bb5c4817e5
commit 3b9468e8b9
52 changed files with 553 additions and 133 deletions

View file

@ -1289,7 +1289,7 @@
rules:
- if: '$DAST_DISABLED || $GITLAB_FEATURES !~ /\bdast\b/'
when: never
- <<: *if-default-branch-schedule-nightly
- <<: *if-dot-com-ee-nightly-schedule
allow_failure: true
.reports:rules:package_hunter-yarn:

View file

@ -826,5 +826,7 @@ Individual Cool Widget replication and verification data should now be available
feature_flag: :geo_cool_widget_replication # REMOVE THIS LINE
```
- [ ] Run `bundle exec rake gitlab:graphql:compile_docs` after the step above to regenerate the GraphQL docs.
- [ ] Add a row for Cool Widgets to the `Data types` table in [Geo data types support](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/replication/datatypes.md#data-types)
- [ ] Add a row for Cool Widgets to the `Limitations on replication/verification` table in [Geo data types support](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/replication/datatypes.md#limitations-on-replicationverification). If the row already exists, then update it to show that Replication and Verification is released in the current version.

View file

@ -794,5 +794,7 @@ Individual Cool Widget replication and verification data should now be available
feature_flag: :geo_cool_widget_replication # REMOVE THIS LINE
```
- [ ] Run `bundle exec rake gitlab:graphql:compile_docs` after the step above to regenerate the GraphQL docs.
- [ ] Add a row for Cool Widgets to the `Data types` table in [Geo data types support](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/replication/datatypes.md#data-types)
- [ ] Add a row for Cool Widgets to the `Limitations on replication/verification` table in [Geo data types support](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/replication/datatypes.md#limitations-on-replicationverification). If the row already exists, then update it to show that Replication and Verification is released in the current version.

View file

@ -1 +1 @@
14.3.1
14.3.2

View file

@ -315,7 +315,7 @@ export default {
<span
v-if="isJiraIssue"
v-safe-html="jiraLogo"
class="svg-container jira-logo-container"
class="svg-container logo-container"
data-testid="jira-logo"
></span>
{{ referencePath }}

View file

@ -18,7 +18,10 @@ class Admin::ApplicationsController < Admin::ApplicationController
end
def new
@application = Doorkeeper::Application.new
# Default access tokens to expire. This preserves backward compatibility
# with existing applications. This will be removed in 15.0.
# Removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/340848
@application = Doorkeeper::Application.new(expire_access_tokens: true)
end
def edit
@ -55,10 +58,13 @@ class Admin::ApplicationsController < Admin::ApplicationController
@application = ApplicationsFinder.new(id: params[:id]).execute
end
# Only allow a trusted parameter "white list" through.
def permitted_params
super << :trusted
end
def application_params
params
.require(:doorkeeper_application)
.permit(:name, :redirect_uri, :trusted, :scopes, :confidential)
super.tap do |params|
params[:owner] = nil
end
end
end

View file

@ -9,7 +9,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
if @runner.assign_to(@project, current_user)
redirect_to admin_runner_path(@runner)
redirect_to admin_runner_path(@runner), notice: s_('Runners|Runner assigned to project.')
else
redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project'
end
@ -20,7 +20,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController
runner = rp.runner
rp.destroy
redirect_to admin_runner_path(runner), status: :found
redirect_to admin_runner_path(runner), status: :found, notice: s_('Runners|Runner unassigned from project.')
end
private

View file

@ -18,4 +18,14 @@ module OauthApplications
def load_scopes
@scopes ||= Doorkeeper.configuration.scopes
end
def permitted_params
%i{name redirect_uri scopes confidential expire_access_tokens}
end
def application_params
params
.require(:doorkeeper_application)
.permit(*permitted_params)
end
end

View file

@ -54,8 +54,10 @@ module Groups
# https://gitlab.com/gitlab-org/gitlab/-/issues/324187
@applications = @group.oauth_applications.limit(100)
# Don't overwrite a value possibly set by `create`
@application ||= Doorkeeper::Application.new
# Default access tokens to expire. This preserves backward compatibility
# with existing applications. This will be removed in 15.0.
# Removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/340848
@application ||= Doorkeeper::Application.new(expire_access_tokens: true)
end
def set_application
@ -63,12 +65,9 @@ module Groups
end
def application_params
params
.require(:doorkeeper_application)
.permit(:name, :redirect_uri, :scopes, :confidential)
.tap do |params|
params[:owner] = @group
end
super.tap do |params|
params[:owner] = @group
end
end
end
end

View file

@ -25,7 +25,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
end
def create
@application = Applications::CreateService.new(current_user, create_application_params).execute(request)
@application = Applications::CreateService.new(current_user, application_params).execute(request)
if @application.persisted?
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
@ -51,8 +51,10 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
@authorized_anonymous_tokens = @authorized_tokens.reject(&:application)
@authorized_apps = @authorized_tokens.map(&:application).uniq.reject(&:nil?)
# Don't overwrite a value possibly set by `create`
@application ||= Doorkeeper::Application.new
# Default access tokens to expire. This preserves backward compatibility
# with existing applications. This will be removed in 15.0.
# Removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/340848
@application ||= Doorkeeper::Application.new(expire_access_tokens: true)
end
# Override Doorkeeper to scope to the current user
@ -64,8 +66,8 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
render "errors/not_found", layout: "errors", status: :not_found
end
def create_application_params
application_params.tap do |params|
def application_params
super.tap do |params|
params[:owner] = current_user
end
end

View file

@ -15,7 +15,7 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
path = project_runners_path(project)
if @runner.assign_to(project, current_user)
redirect_to path
redirect_to path, notice: s_('Runners|Runner assigned to project.')
else
assign_to_messages = @runner.errors.messages[:assign_to]
alert = assign_to_messages&.join(',') || 'Failed adding runner to project'
@ -28,6 +28,6 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
runner_project = project.runner_projects.find(params[:id])
runner_project.destroy
redirect_to project_runners_path(project), status: :found
redirect_to project_runners_path(project), status: :found, notice: s_('Runners|Runner unassigned from project.')
end
end

View file

@ -26,6 +26,7 @@ module Enums
pipeline_loop_detected: 17,
no_matching_runner: 18, # not used anymore, but cannot be deleted because of old data
trace_size_exceeded: 19,
builds_disabled: 20,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
invalid_bridge_trigger: 1_003,

View file

@ -27,7 +27,8 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
user_blocked: 'The user who created this job is blocked',
ci_quota_exceeded: 'No more CI minutes available',
no_matching_runner: 'No matching runner available',
trace_size_exceeded: 'The job log size limit was reached'
trace_size_exceeded: 'The job log size limit was reached',
builds_disabled: 'The CI/CD is disabled for this project'
}.freeze
private_constant :CALLOUT_FAILURE_MESSAGES

View file

@ -271,6 +271,15 @@ module Ci
missing_dependency_failure: -> (build, _) { !build.has_valid_build_dependencies? },
runner_unsupported: -> (build, params) { !build.supported_runner?(params.dig(:info, :features)) },
archived_failure: -> (build, _) { build.archived? }
}.merge(builds_enabled_checks)
end
def builds_enabled_checks
return {} unless ::Feature.enabled?(:ci_queueing_builds_enabled_checks, runner, default_enabled: :yaml)
{
project_deleted: -> (build, _) { build.project.pending_delete? },
builds_disabled: -> (build, _) { !build.project.builds_enabled? }
}
end
end

View file

@ -12,18 +12,19 @@ module Packages
if @tag_name.present?
@tag_name.delete_prefix('v')
elsif @branch_name.present?
branch_sufix_or_prefix(@branch_name.match(Gitlab::Regex.composer_package_version_regex))
branch_suffix_or_prefix(@branch_name.match(Gitlab::Regex.composer_package_version_regex))
end
end
private
def branch_sufix_or_prefix(match)
def branch_suffix_or_prefix(match)
if match
if match.captures[1] == '.x'
match.captures[0] + '-dev'
captures = match.captures.reject(&:blank?)
if captures[-1] == '.x'
captures[0] + '-dev'
else
match.captures[0] + '.x-dev'
captures[0] + '.x-dev'
end
else
"dev-#{@branch_name}"

View file

@ -33,6 +33,14 @@
%span.form-text.text-muted
= _('The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.')
= content_tag :div, class: 'form-group row' do
.col-sm-2.col-form-label.pt-0
= f.label :expire_access_tokens
.col-sm-10
= f.check_box :expire_access_tokens
%span.form-text.text-muted
= _('Access tokens expire after 2 hours. A refresh token may be used at any time to generate a new access token. Non-expiring access tokens are deprecated. Clear this setting to enable backward compatibility.')
.form-group.row
.col-sm-2.col-form-label.pt-0
= f.label :scopes

View file

@ -18,6 +18,12 @@
%span.form-text.text-muted
= _('The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.')
.form-group.form-check
= f.check_box :expire_access_tokens, class: 'form-check-input'
= f.label :expire_access_tokens, class: 'label-bold form-check-label'
%span.form-text.text-muted
= _('Access tokens expire after 2 hours. A refresh token may be used at any time to generate a new access token. Non-expiring access tokens are deprecated. Clear this setting to enable backward compatibility.')
.form-group
= f.label :scopes, class: 'label-bold'
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: @application, scopes: @scopes

View file

@ -0,0 +1,8 @@
---
name: ci_queueing_builds_enabled_checks
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70581
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341131
milestone: '14.4'
type: development
group: group::pipeline execution
default_enabled: false

View file

@ -38,8 +38,11 @@ Doorkeeper.configure do
# authorization_code_expires_in 10.minutes
# Access token expiration time (default 2 hours).
# If you want to disable expiration, set this to nil.
access_token_expires_in nil
# Until 15.0, applications can opt-out of expiring tokens.
# Removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/340848
custom_access_token_expires_in do |context|
context.client&.expire_access_tokens ? 2.hours : Float::INFINITY
end
# Reuse access token for the same resource owner within an application (disabled by default)
# Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383

View file

@ -0,0 +1,23 @@
---
key_path: counts_monthly.promoted_issues
name: count_promoted_issues
description: Count of issues promoted to epics
product_section: growth
product_stage: growth
product_group: group::product intelligence
product_category: collection
value_type: number
status: active
milestone: "14.3"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70485
time_frame: 28d
data_source: database
data_category: optional
performance_indicator_type: []
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddIterationCadenceIdToIssueBoards < Gitlab::Database::Migration[1.0]
enable_lock_retries!
def change
add_column :boards, :iteration_cadence_id, :bigint
end
end

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddExpireAccessTokensToDoorkeeperApplication < Gitlab::Database::Migration[1.0]
def change
add_column :oauth_applications, :expire_access_tokens, :boolean, default: false, null: false
end
end

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
class AddFkToIterationCadenceIdOnBoards < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
INDEX_NAME = 'index_boards_on_iteration_cadence_id'
def up
add_concurrent_index :boards, :iteration_cadence_id, name: INDEX_NAME
add_concurrent_foreign_key :boards, :iterations_cadences, column: :iteration_cadence_id
end
def down
with_lock_retries do
remove_foreign_key_if_exists :boards, column: :iteration_cadence_id
end
remove_concurrent_index_by_name :boards, INDEX_NAME
end
end

View file

@ -0,0 +1,49 @@
# frozen_string_literal: true
class BackfillCadenceIdForBoardsScopedToIteration < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
BATCH_SIZE = 1000
DELAY = 2.minutes.to_i
MIGRATION = 'BackfillIterationCadenceIdForBoards'
class MigrationBoard < ApplicationRecord
include EachBatch
self.table_name = 'boards'
end
def up
schedule_backfill_group_boards
schedule_backfill_project_boards
end
def down
MigrationBoard.where.not(iteration_cadence_id: nil).each_batch(of: BATCH_SIZE) do |batch, index|
range = batch.pluck(Arel.sql('MIN(id)'), Arel.sql('MAX(id)')).first
delay = index * DELAY
migrate_in(delay, MIGRATION, ['none', 'down', *range])
end
end
private
def schedule_backfill_project_boards
MigrationBoard.where(iteration_id: -4).where.not(project_id: nil).where(iteration_cadence_id: nil).each_batch(of: BATCH_SIZE) do |batch, index|
range = batch.pluck(Arel.sql('MIN(id)'), Arel.sql('MAX(id)')).first
delay = index * DELAY
migrate_in(delay, MIGRATION, ['project', 'up', *range])
end
end
def schedule_backfill_group_boards
MigrationBoard.where(iteration_id: -4).where.not(group_id: nil).where(iteration_cadence_id: nil).each_batch(of: BATCH_SIZE) do |batch, index|
range = batch.pluck(Arel.sql('MIN(id)'), Arel.sql('MAX(id)')).first
delay = index * DELAY
migrate_in(delay, MIGRATION, ['group', 'up', *range])
end
end
end

View file

@ -0,0 +1 @@
d9c7cc7721b28cbd442bf40255ecfbd20d0abf4cd31631c150ebdc05c76062be

View file

@ -0,0 +1 @@
b97b77aef61db2e51106ac090f5511a67fa85be8f3741f618fe03c8c03ecd88c

View file

@ -0,0 +1 @@
fd7aef11635bc4c5d6b9346dbed90f6c114da7b7a33744083e8610f3850e4736

View file

@ -0,0 +1 @@
508b8d4608d28b2a12cf429262c3dd336e130013a41e51ff6c95027ece1540e5

View file

@ -10894,7 +10894,8 @@ CREATE TABLE boards (
weight integer,
hide_backlog_list boolean DEFAULT false NOT NULL,
hide_closed_list boolean DEFAULT false NOT NULL,
iteration_id bigint
iteration_id bigint,
iteration_cadence_id bigint
);
CREATE TABLE boards_epic_board_labels (
@ -16385,7 +16386,8 @@ CREATE TABLE oauth_applications (
owner_id integer,
owner_type character varying,
trusted boolean DEFAULT false NOT NULL,
confidential boolean DEFAULT true NOT NULL
confidential boolean DEFAULT true NOT NULL,
expire_access_tokens boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE oauth_applications_id_seq
@ -24405,6 +24407,8 @@ CREATE INDEX index_boards_epic_user_preferences_on_user_id ON boards_epic_user_p
CREATE INDEX index_boards_on_group_id ON boards USING btree (group_id);
CREATE INDEX index_boards_on_iteration_cadence_id ON boards USING btree (iteration_cadence_id);
CREATE INDEX index_boards_on_iteration_id ON boards USING btree (iteration_id);
CREATE INDEX index_boards_on_milestone_id ON boards USING btree (milestone_id);
@ -27911,6 +27915,9 @@ ALTER TABLE ONLY alert_management_alerts
ALTER TABLE ONLY identities
ADD CONSTRAINT fk_aade90f0fc FOREIGN KEY (saml_provider_id) REFERENCES saml_providers(id) ON DELETE CASCADE;
ALTER TABLE ONLY boards
ADD CONSTRAINT fk_ab0a250ff6 FOREIGN KEY (iteration_cadence_id) REFERENCES iterations_cadences(id) ON DELETE CASCADE;
ALTER TABLE ONLY dep_ci_build_trace_sections
ADD CONSTRAINT fk_ab7c104e26 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;

View file

@ -1075,6 +1075,7 @@ Input type: `CreateBoardInput`
| <a id="mutationcreateboardgrouppath"></a>`groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. |
| <a id="mutationcreateboardhidebackloglist"></a>`hideBacklogList` | [`Boolean`](#boolean) | Whether or not backlog list is hidden. |
| <a id="mutationcreateboardhideclosedlist"></a>`hideClosedList` | [`Boolean`](#boolean) | Whether or not closed list is hidden. |
| <a id="mutationcreateboarditerationcadenceid"></a>`iterationCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | ID of iteration cadence to be assigned to the board. |
| <a id="mutationcreateboarditerationid"></a>`iterationId` | [`IterationID`](#iterationid) | ID of iteration to be assigned to the board. |
| <a id="mutationcreateboardlabelids"></a>`labelIds` | [`[LabelID!]`](#labelid) | IDs of labels to be added to the board. |
| <a id="mutationcreateboardlabels"></a>`labels` | [`[String!]`](#string) | Labels of the issue. |
@ -4122,6 +4123,7 @@ Input type: `UpdateBoardInput`
| <a id="mutationupdateboardhidebackloglist"></a>`hideBacklogList` | [`Boolean`](#boolean) | Whether or not backlog list is hidden. |
| <a id="mutationupdateboardhideclosedlist"></a>`hideClosedList` | [`Boolean`](#boolean) | Whether or not closed list is hidden. |
| <a id="mutationupdateboardid"></a>`id` | [`BoardID!`](#boardid) | Board global ID. |
| <a id="mutationupdateboarditerationcadenceid"></a>`iterationCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | ID of iteration cadence to be assigned to the board. |
| <a id="mutationupdateboarditerationid"></a>`iterationId` | [`IterationID`](#iterationid) | ID of iteration to be assigned to the board. |
| <a id="mutationupdateboardlabelids"></a>`labelIds` | [`[LabelID!]`](#labelid) | IDs of labels to be added to the board. |
| <a id="mutationupdateboardlabels"></a>`labels` | [`[String!]`](#string) | Labels of the issue. |
@ -7897,6 +7899,7 @@ Represents a project or group issue board.
| <a id="boardhideclosedlist"></a>`hideClosedList` | [`Boolean`](#boolean) | Whether or not closed list is hidden. |
| <a id="boardid"></a>`id` | [`ID!`](#id) | ID (global ID) of the board. |
| <a id="boarditeration"></a>`iteration` | [`Iteration`](#iteration) | Board iteration. |
| <a id="boarditerationcadence"></a>`iterationCadence` | [`IterationCadence`](#iterationcadence) | Board iteration cadence. |
| <a id="boardlabels"></a>`labels` | [`LabelConnection`](#labelconnection) | Labels of the board. (see [Connections](#connections)) |
| <a id="boardmilestone"></a>`milestone` | [`Milestone`](#milestone) | Board milestone. |
| <a id="boardname"></a>`name` | [`String`](#string) | Name of the board. |

View file

@ -223,7 +223,7 @@ the contribution acceptance criteria below:
## Definition of done
If you contribute to GitLab please know that changes involve more than just
If you contribute to GitLab, please know that changes involve more than just
code. We use the following [definition of done](https://www.agilealliance.org/glossary/definition-of-done).
To reach the definition of done, the merge request must create no regressions and meet all these criteria:
@ -231,7 +231,7 @@ To reach the definition of done, the merge request must create no regressions an
- Verified as working for self-managed instances.
If a regression occurs, we prefer you revert the change. We break the definition of done into two phases: [MR Merge](#mr-merge) and [Production use](#production-use).
Your contribution is not *done* until you have made sure it meets all of these
Your contribution is *incomplete* until you have made sure it meets all of these
requirements.
### MR Merge
@ -246,18 +246,24 @@ requirements.
1. [Secure coding guidelines](https://gitlab.com/gitlab-com/gl-security/security-guidelines) have been followed.
1. [Documented](../documentation/index.md) in the `/doc` directory.
1. [Changelog entry added](../changelog.md), if necessary.
1. Reviewed by relevant reviewers and all concerns are addressed for Availability, Regressions, Security. Documentation reviews should take place as soon as possible, but they should not block a merge request.
1. Reviewed by relevant reviewers, and all concerns are addressed for Availability, Regressions, and Security. Documentation reviews should take place as soon as possible, but they should not block a merge request.
1. The [MR acceptance checklist](../code_review.md#acceptance-checklist) has been checked as confirmed in the MR.
1. Create an issue in the [infrastructure issue tracker](https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues) to inform the Infrastructure department when your contribution is changing default settings or introduces a new setting, if relevant.
1. [Black-box tests/end-to-end tests](../testing_guide/testing_levels.md#black-box-tests-at-the-system-level-aka-end-to-end-tests)
added if required. Please contact [the quality team](https://about.gitlab.com/handbook/engineering/quality/#teams)
with any questions.
1. The change is tested in a review app where possible and if appropriate.
1. The new feature does not degrade the user experience of the product.
1. An agreed upon rollout plan.
1. The change is evaluated to [limit the impact of far-reaching work](https://about.gitlab.com/handbook/engineering/development/#reducing-the-impact-of-far-reaching-work).
1. An agreed-upon rollout plan.
1. Merged by a project maintainer.
### Production use
1. Confirmed to be working in staging before implementing the change in production, where possible.
1. Confirmed to be working in the production with no new [Sentry](https://about.gitlab.com/handbook/engineering/#sentry) errors after the contribution is deployed.
1. Confirmed that the rollout plan has been completed.
1. If there is a performance risk in the change, I have analyzed the performance of the system before and after the change.
1. *If the merge request uses feature flags, per-project or per-group enablement, and a staged rollout:*
- Confirmed to be working on GitLab projects.
- Confirmed to be working at each stage for all projects added.

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# rubocop: disable Style/Documentation
class BackfillIterationCadenceIdForBoards
def perform(*args)
end
end
end
end
Gitlab::BackgroundMigration::BackfillIterationCadenceIdForBoards.prepend_mod_with('Gitlab::BackgroundMigration::BackfillIterationCadenceIdForBoards')

View file

@ -32,7 +32,8 @@ module Gitlab
user_blocked: 'pipeline user was blocked',
ci_quota_exceeded: 'no more CI minutes available',
no_matching_runner: 'no matching runner available',
trace_size_exceeded: 'log size limit exceeded'
trace_size_exceeded: 'log size limit exceeded',
builds_disabled: 'project builds are disabled'
}.freeze
private_constant :REASONS

View file

@ -70,7 +70,9 @@ module Gitlab
# If the lock was not acquired within the retry period, a last attempt is made without using +lock_timeout+.
#
# In order to retry the block, the method wraps the block into a transaction.
# Note it cannot be used inside an already open transaction and will raise an error in that case.
#
# When called inside an open transaction it will execute the block directly if lock retries are enabled
# with `enable_lock_retries!` at migration level, otherwise it will raise an error.
#
# ==== Examples
# # Invoking without parameters
@ -101,14 +103,19 @@ module Gitlab
# * +env+ - [Hash] custom environment hash, see the example with `DISABLE_LOCK_RETRIES`
def with_lock_retries(*args, **kwargs, &block)
if transaction_open?
raise <<~EOF
if enable_lock_retries?
Gitlab::AppLogger.warn 'Lock retries already enabled, executing the block directly'
yield
else
raise <<~EOF
#{__callee__} can not be run inside an already open transaction
Use migration-level lock retries instead, see https://docs.gitlab.com/ee/development/migration_style_guide.html#retry-mechanism-when-acquiring-database-locks
EOF
EOF
end
else
super(*args, **kwargs.merge(allow_savepoints: false), &block)
end
super(*args, **kwargs.merge(allow_savepoints: false), &block)
end
# Renames a column without requiring downtime.

View file

@ -400,6 +400,7 @@ excluded_attributes:
boards:
- :milestone_id
- :iteration_id
- :iteration_cadence_id
lists:
- :board_id
- :label_id

View file

@ -82,9 +82,21 @@ module Gitlab
end
def self.configure_throttles(rack_attack)
throttle_or_track(rack_attack, 'throttle_unauthenticated_api', Gitlab::Throttle.unauthenticated_api_options) do |req|
if req.throttle_unauthenticated_api?
req.ip
# Each of these settings follows the same pattern of specifying separate
# authenticated and unauthenticated rates via settings
Gitlab::Throttle::REGULAR_THROTTLES.each do |throttle|
unauthenticated_options = Gitlab::Throttle.options(throttle, authenticated: false)
throttle_or_track(rack_attack, "throttle_unauthenticated_#{throttle}", unauthenticated_options) do |req|
if req.throttle?(throttle, authenticated: false)
req.ip
end
end
authenticated_options = Gitlab::Throttle.options(throttle, authenticated: true)
throttle_or_track(rack_attack, "throttle_authenticated_#{throttle}", authenticated_options) do |req|
if req.throttle?(throttle, authenticated: true)
req.throttled_user_id([:api])
end
end
end
@ -94,12 +106,6 @@ module Gitlab
end
end
throttle_or_track(rack_attack, 'throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req|
if req.throttle_authenticated_api?
req.throttled_user_id([:api])
end
end
# Product analytics feature is in experimental stage.
# At this point we want to limit amount of events registered
# per application (aid stands for application id).
@ -133,36 +139,12 @@ module Gitlab
end
end
throttle_or_track(rack_attack, 'throttle_unauthenticated_packages_api', Gitlab::Throttle.unauthenticated_packages_api_options) do |req|
if req.throttle_unauthenticated_packages_api?
req.ip
end
end
throttle_or_track(rack_attack, 'throttle_authenticated_packages_api', Gitlab::Throttle.authenticated_packages_api_options) do |req|
if req.throttle_authenticated_packages_api?
req.throttled_user_id([:api])
end
end
throttle_or_track(rack_attack, 'throttle_authenticated_git_lfs', Gitlab::Throttle.throttle_authenticated_git_lfs_options) do |req|
if req.throttle_authenticated_git_lfs?
req.throttled_user_id([:api])
end
end
throttle_or_track(rack_attack, 'throttle_unauthenticated_files_api', Gitlab::Throttle.unauthenticated_files_api_options) do |req|
if req.throttle_unauthenticated_files_api?
req.ip
end
end
throttle_or_track(rack_attack, 'throttle_authenticated_files_api', Gitlab::Throttle.authenticated_files_api_options) do |req|
if req.throttle_authenticated_files_api?
req.throttled_user_id([:api])
end
end
rack_attack.safelist('throttle_bypass_header') do |req|
Gitlab::Throttle.bypass_header.present? &&
req.get_header(Gitlab::Throttle.bypass_header) == '1'

View file

@ -60,6 +60,12 @@ module Gitlab
path =~ protected_paths_regex
end
def throttle?(throttle, authenticated:)
fragment = Gitlab::Throttle.throttle_fragment!(throttle, authenticated: authenticated)
__send__("#{fragment}?") # rubocop:disable GitlabSecurity/PublicSend
end
def throttle_unauthenticated_api?
api_request? &&
!should_be_skipped? &&

View file

@ -21,7 +21,8 @@ module Gitlab
end
def composer_package_version_regex
@composer_package_version_regex ||= %r{^v?(\d+(\.(\d+|x))*(-.+)?)}.freeze
# see https://github.com/composer/semver/blob/31f3ea725711245195f62e54ffa402d8ef2fdba9/src/VersionParser.php#L215
@composer_package_version_regex ||= %r{\Av?((\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?)?\z}.freeze
end
def composer_dev_version_regex

View file

@ -4,6 +4,11 @@ module Gitlab
class Throttle
DEFAULT_RATE_LIMITING_RESPONSE_TEXT = 'Retry later'
# Each of these settings follows the same pattern of specifying separate
# authenticated and unauthenticated rates via settings. New throttles should
# ideally be regular as well.
REGULAR_THROTTLES = [:api, :packages_api, :files_api].freeze
def self.settings
Gitlab::CurrentSettings.current_application_settings
end
@ -24,28 +29,38 @@ module Gitlab
"HTTP_#{env_value.upcase.tr('-', '_')}"
end
def self.unauthenticated_api_options
limit_proc = proc { |req| settings.throttle_unauthenticated_api_requests_per_period }
period_proc = proc { |req| settings.throttle_unauthenticated_api_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
class << self
def options(throttle, authenticated:)
fragment = throttle_fragment!(throttle, authenticated: authenticated)
# rubocop:disable GitlabSecurity/PublicSend
limit_proc = proc { |req| settings.public_send("#{fragment}_requests_per_period") }
period_proc = proc { |req| settings.public_send("#{fragment}_period_in_seconds").seconds }
# rubocop:enable GitlabSecurity/PublicSend
{ limit: limit_proc, period: period_proc }
end
def throttle_fragment!(throttle, authenticated:)
raise("Unknown throttle: #{throttle}") unless REGULAR_THROTTLES.include?(throttle)
"throttle_#{'un' unless authenticated}authenticated_#{throttle}"
end
end
def self.unauthenticated_web_options
# TODO: Columns will be renamed in https://gitlab.com/gitlab-org/gitlab/-/issues/340031
# Once this is done, web can be made into a regular throttle
limit_proc = proc { |req| settings.throttle_unauthenticated_requests_per_period }
period_proc = proc { |req| settings.throttle_unauthenticated_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.authenticated_api_options
limit_proc = proc { |req| settings.throttle_authenticated_api_requests_per_period }
period_proc = proc { |req| settings.throttle_authenticated_api_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.authenticated_web_options
limit_proc = proc { |req| settings.throttle_authenticated_web_requests_per_period }
period_proc = proc { |req| settings.throttle_authenticated_web_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
@ -56,20 +71,6 @@ module Gitlab
{ limit: limit_proc, period: period_proc }
end
def self.unauthenticated_packages_api_options
limit_proc = proc { |req| settings.throttle_unauthenticated_packages_api_requests_per_period }
period_proc = proc { |req| settings.throttle_unauthenticated_packages_api_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.authenticated_packages_api_options
limit_proc = proc { |req| settings.throttle_authenticated_packages_api_requests_per_period }
period_proc = proc { |req| settings.throttle_authenticated_packages_api_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.throttle_authenticated_git_lfs_options
limit_proc = proc { |req| settings.throttle_authenticated_git_lfs_requests_per_period }
period_proc = proc { |req| settings.throttle_authenticated_git_lfs_period_in_seconds.seconds }
@ -77,20 +78,6 @@ module Gitlab
{ limit: limit_proc, period: period_proc }
end
def self.unauthenticated_files_api_options
limit_proc = proc { |req| settings.throttle_unauthenticated_files_api_requests_per_period }
period_proc = proc { |req| settings.throttle_unauthenticated_files_api_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.authenticated_files_api_options
limit_proc = proc { |req| settings.throttle_authenticated_files_api_requests_per_period }
period_proc = proc { |req| settings.throttle_authenticated_files_api_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.rate_limiting_response_text
(settings.rate_limiting_response_text.presence || DEFAULT_RATE_LIMITING_RESPONSE_TEXT) + "\n"
end

View file

@ -1741,6 +1741,9 @@ msgstr ""
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access tokens expire after 2 hours. A refresh token may be used at any time to generate a new access token. Non-expiring access tokens are deprecated. Clear this setting to enable backward compatibility."
msgstr ""
msgid "AccessDropdown|Deploy Keys"
msgstr ""
@ -29142,6 +29145,9 @@ msgstr ""
msgid "Runners|Runner #%{runner_id}"
msgstr ""
msgid "Runners|Runner assigned to project."
msgstr ""
msgid "Runners|Runner is offline, last contact was %{runner_contact} ago"
msgstr ""
@ -29154,6 +29160,9 @@ msgstr ""
msgid "Runners|Runner registration"
msgstr ""
msgid "Runners|Runner unassigned from project."
msgstr ""
msgid "Runners|Runners"
msgstr ""

View file

@ -24,6 +24,7 @@ module QA
Resource::MergeRequest.fabricate_via_browser_ui! do |merge_request|
merge_request.project = project
merge_request.title = merge_request_title
merge_request.assignee = 'me'
merge_request.description = merge_request_description
end

View file

@ -356,6 +356,7 @@ RSpec.describe "Admin Runners" do
assigned_project = page.find('[data-testid="assigned-projects"]')
expect(page).to have_content('Runner assigned to project.')
expect(assigned_project).to have_content(@project2.path)
end
end
@ -399,13 +400,14 @@ RSpec.describe "Admin Runners" do
visit admin_runner_path(runner)
end
it 'enables specific runner for project' do
it 'removed specific runner from project' do
within '[data-testid="assigned-projects"]' do
click_on 'Disable'
end
new_runner_project = page.find('[data-testid="unassigned-projects"]')
expect(page).to have_content('Runner unassigned from project.')
expect(new_runner_project).to have_content(@project1.path)
end
end

View file

@ -293,5 +293,33 @@ RSpec.describe Gitlab::Database::MigrationHelpers::V2 do
model.with_lock_retries(env: env, logger: in_memory_logger) { }
end
context 'when in transaction' do
before do
allow(model).to receive(:transaction_open?).and_return(true)
end
context 'when lock retries are enabled' do
before do
allow(model).to receive(:enable_lock_retries?).and_return(true)
end
it 'does not use Gitlab::Database::WithLockRetries and executes the provided block directly' do
expect(Gitlab::Database::WithLockRetries).not_to receive(:new)
expect(model.with_lock_retries(env: env, logger: in_memory_logger) { :block_result }).to eq(:block_result)
end
end
context 'when lock retries are not enabled' do
before do
allow(model).to receive(:enable_lock_retries?).and_return(false)
end
it 'raises an error' do
expect { model.with_lock_retries(env: env, logger: in_memory_logger) { } }.to raise_error /can not be run inside an already open transaction/
end
end
end
end
end

View file

@ -683,6 +683,7 @@ boards:
- destroyable_lists
- milestone
- iteration
- iteration_cadence
- board_labels
- board_assignee
- assignee

View file

@ -762,6 +762,7 @@ Board:
- group_id
- milestone_id
- iteration_id
- iteration_cadence_id
- weight
- name
- hide_backlog_list

View file

@ -10,19 +10,19 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
let(:throttles) do
{
throttle_unauthenticated_api: Gitlab::Throttle.unauthenticated_api_options,
throttle_unauthenticated_api: Gitlab::Throttle.options(:api, authenticated: false),
throttle_authenticated_api: Gitlab::Throttle.options(:api, authenticated: true),
throttle_unauthenticated_web: Gitlab::Throttle.unauthenticated_web_options,
throttle_authenticated_api: Gitlab::Throttle.authenticated_api_options,
throttle_product_analytics_collector: { limit: 100, period: 60 },
throttle_authenticated_web: Gitlab::Throttle.authenticated_web_options,
throttle_product_analytics_collector: { limit: 100, period: 60 },
throttle_unauthenticated_protected_paths: Gitlab::Throttle.protected_paths_options,
throttle_authenticated_protected_paths_api: Gitlab::Throttle.protected_paths_options,
throttle_authenticated_protected_paths_web: Gitlab::Throttle.protected_paths_options,
throttle_unauthenticated_packages_api: Gitlab::Throttle.unauthenticated_packages_api_options,
throttle_authenticated_packages_api: Gitlab::Throttle.authenticated_packages_api_options,
throttle_unauthenticated_packages_api: Gitlab::Throttle.options(:packages_api, authenticated: false),
throttle_authenticated_packages_api: Gitlab::Throttle.options(:packages_api, authenticated: true),
throttle_authenticated_git_lfs: Gitlab::Throttle.throttle_authenticated_git_lfs_options,
throttle_unauthenticated_files_api: Gitlab::Throttle.unauthenticated_files_api_options,
throttle_authenticated_files_api: Gitlab::Throttle.authenticated_files_api_options
throttle_unauthenticated_files_api: Gitlab::Throttle.options(:files_api, authenticated: false),
throttle_authenticated_files_api: Gitlab::Throttle.options(:files_api, authenticated: true)
}
end

View file

@ -924,4 +924,25 @@ RSpec.describe Gitlab::Regex do
it { is_expected.not_to match('/api/v4/groups/1234/packages/debian/dists/stable/Release.gpg') }
it { is_expected.not_to match('/api/v4/groups/1234/packages/debian/pool/compon/a/pkg/file.name') }
end
describe '.composer_package_version_regex' do
subject { described_class.composer_package_version_regex }
it { is_expected.to match('v1.2.3') }
it { is_expected.to match('v1.2.x') }
it { is_expected.to match('v1.2.X') }
it { is_expected.to match('1.2.3') }
it { is_expected.to match('1') }
it { is_expected.to match('v1') }
it { is_expected.to match('1.2') }
it { is_expected.to match('v1.2') }
it { is_expected.not_to match('1.2.3-beta') }
it { is_expected.not_to match('1.2.x-beta') }
it { is_expected.not_to match('1.2.X-beta') }
it { is_expected.not_to match('1.2.3-alpha.3') }
it { is_expected.not_to match('1./2.3') }
it { is_expected.not_to match('v1./2.3') }
it { is_expected.not_to match('../../../../../1.2.3') }
it { is_expected.not_to match('%2e%2e%2f1.2.3') }
end
end

View file

@ -0,0 +1,109 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
# require Rails.root.join('db', 'post_migrate', '20210825193652_backfill_candence_id_for_boards_scoped_to_iteration.rb')
RSpec.describe BackfillCadenceIdForBoardsScopedToIteration, :migration do
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
let(:iterations_cadences) { table(:iterations_cadences) }
let(:boards) { table(:boards) }
let!(:group) { namespaces.create!(name: 'group1', path: 'group1', type: 'Group') }
let!(:cadence) { iterations_cadences.create!(title: 'group cadence', group_id: group.id, start_date: Time.current) }
let!(:project) { projects.create!(name: 'gitlab1', path: 'gitlab1', namespace_id: group.id, visibility_level: 0) }
let!(:project_board1) { boards.create!(name: 'Project Dev1', project_id: project.id) }
let!(:project_board2) { boards.create!(name: 'Project Dev2', project_id: project.id, iteration_id: -4) }
let!(:project_board3) { boards.create!(name: 'Project Dev3', project_id: project.id, iteration_id: -4) }
let!(:project_board4) { boards.create!(name: 'Project Dev4', project_id: project.id, iteration_id: -4) }
let!(:group_board1) { boards.create!(name: 'Group Dev1', group_id: group.id) }
let!(:group_board2) { boards.create!(name: 'Group Dev2', group_id: group.id, iteration_id: -4) }
let!(:group_board3) { boards.create!(name: 'Group Dev3', group_id: group.id, iteration_id: -4) }
let!(:group_board4) { boards.create!(name: 'Group Dev4', group_id: group.id, iteration_id: -4) }
describe '#up' do
it 'schedules background migrations' do
Sidekiq::Testing.fake! do
freeze_time do
described_class.new.up
migration = described_class::MIGRATION
expect(migration).to be_scheduled_delayed_migration(2.minutes, 'group', 'up', group_board2.id, group_board4.id)
expect(migration).to be_scheduled_delayed_migration(2.minutes, 'project', 'up', project_board2.id, project_board4.id)
expect(BackgroundMigrationWorker.jobs.size).to eq 2
end
end
end
context 'in batches' do
before do
stub_const('BackfillCadenceIdForBoardsScopedToIteration::BATCH_SIZE', 2)
end
it 'schedules background migrations' do
Sidekiq::Testing.fake! do
freeze_time do
described_class.new.up
migration = described_class::MIGRATION
expect(migration).to be_scheduled_delayed_migration(2.minutes, 'group', 'up', group_board2.id, group_board3.id)
expect(migration).to be_scheduled_delayed_migration(4.minutes, 'group', 'up', group_board4.id, group_board4.id)
expect(migration).to be_scheduled_delayed_migration(2.minutes, 'project', 'up', project_board2.id, project_board3.id)
expect(migration).to be_scheduled_delayed_migration(4.minutes, 'project', 'up', project_board4.id, project_board4.id)
expect(BackgroundMigrationWorker.jobs.size).to eq 4
end
end
end
end
end
describe '#down' do
let!(:project_board1) { boards.create!(name: 'Project Dev1', project_id: project.id) }
let!(:project_board2) { boards.create!(name: 'Project Dev2', project_id: project.id, iteration_cadence_id: cadence.id) }
let!(:project_board3) { boards.create!(name: 'Project Dev3', project_id: project.id, iteration_id: -4, iteration_cadence_id: cadence.id) }
let!(:project_board4) { boards.create!(name: 'Project Dev4', project_id: project.id, iteration_id: -4, iteration_cadence_id: cadence.id) }
let!(:group_board1) { boards.create!(name: 'Group Dev1', group_id: group.id) }
let!(:group_board2) { boards.create!(name: 'Group Dev2', group_id: group.id, iteration_cadence_id: cadence.id) }
let!(:group_board3) { boards.create!(name: 'Group Dev3', group_id: group.id, iteration_id: -4, iteration_cadence_id: cadence.id) }
let!(:group_board4) { boards.create!(name: 'Group Dev4', group_id: group.id, iteration_id: -4, iteration_cadence_id: cadence.id) }
it 'schedules background migrations' do
Sidekiq::Testing.fake! do
freeze_time do
described_class.new.down
migration = described_class::MIGRATION
expect(migration).to be_scheduled_delayed_migration(2.minutes, 'none', 'down', project_board2.id, group_board4.id)
expect(BackgroundMigrationWorker.jobs.size).to eq 1
end
end
end
context 'in batches' do
before do
stub_const('BackfillCadenceIdForBoardsScopedToIteration::BATCH_SIZE', 2)
end
it 'schedules background migrations' do
Sidekiq::Testing.fake! do
freeze_time do
described_class.new.down
migration = described_class::MIGRATION
expect(migration).to be_scheduled_delayed_migration(2.minutes, 'none', 'down', project_board2.id, project_board3.id)
expect(migration).to be_scheduled_delayed_migration(4.minutes, 'none', 'down', project_board4.id, group_board2.id)
expect(migration).to be_scheduled_delayed_migration(6.minutes, 'none', 'down', group_board3.id, group_board4.id)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
end
end
end
end

View file

@ -55,5 +55,29 @@ RSpec.describe 'OAuth Tokens requests' do
expect(json_response['access_token']).not_to be_nil
end
context 'when the application is configured to use expiring tokens' do
before do
application.update!(expire_access_tokens: true)
end
it 'generates an access token with an expiration' do
request_access_token(user)
expect(json_response['expires_in']).not_to be_nil
end
end
context 'when the application is configured not to use expiring tokens' do
before do
application.update!(expire_access_tokens: false)
end
it 'generates an access token without an expiration' do
request_access_token(user)
expect(json_response.key?('expires_in')).to eq(false)
end
end
end
end

View file

@ -87,12 +87,30 @@ module Ci
end
context 'for specific runner' do
before do
stub_feature_flags(ci_pending_builds_project_runners_decoupling: false)
context 'with FF disabled' do
before do
stub_feature_flags(
ci_pending_builds_project_runners_decoupling: false,
ci_queueing_builds_enabled_checks: false)
end
it 'does not pick a build' do
expect(execute(specific_runner)).to be_nil
end
end
it 'does not pick a build' do
expect(execute(specific_runner)).to be_nil
context 'with FF enabled' do
before do
stub_feature_flags(
ci_pending_builds_project_runners_decoupling: true,
ci_queueing_builds_enabled_checks: true)
end
it 'does not pick a build' do
expect(execute(specific_runner)).to be_nil
expect(pending_job.reload).to be_failed
expect(pending_job.queuing_entry).to be_nil
end
end
end
end
@ -246,13 +264,31 @@ module Ci
end
context 'and uses project runner' do
before do
stub_feature_flags(ci_pending_builds_project_runners_decoupling: false)
end
let(:build) { execute(specific_runner) }
it { expect(build).to be_nil }
context 'with FF disabled' do
before do
stub_feature_flags(
ci_pending_builds_project_runners_decoupling: false,
ci_queueing_builds_enabled_checks: false)
end
it { expect(build).to be_nil }
end
context 'with FF enabled' do
before do
stub_feature_flags(
ci_pending_builds_project_runners_decoupling: true,
ci_queueing_builds_enabled_checks: true)
end
it 'does not pick a build' do
expect(build).to be_nil
expect(pending_job.reload).to be_failed
expect(pending_job.queuing_entry).to be_nil
end
end
end
end

View file

@ -12,6 +12,7 @@ RSpec.describe Packages::Composer::VersionParserService do
where(:tagname, :branchname, :expected_version) do
nil | 'master' | 'dev-master'
nil | 'my-feature' | 'dev-my-feature'
nil | '12-feature' | 'dev-12-feature'
nil | 'v1' | '1.x-dev'
nil | 'v1.x' | '1.x-dev'
nil | 'v1.7.x' | '1.7.x-dev'

View file

@ -9,9 +9,11 @@ RSpec.shared_examples 'manage applications' do
visit new_application_path
expect(page).to have_content 'Add new application'
expect(find('#doorkeeper_application_expire_access_tokens')).to be_checked
fill_in :doorkeeper_application_name, with: application_name
fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri
uncheck :doorkeeper_application_expire_access_tokens
check :doorkeeper_application_scopes_read_user
click_on 'Save application'
@ -22,6 +24,8 @@ RSpec.shared_examples 'manage applications' do
click_on 'Edit'
expect(find('#doorkeeper_application_expire_access_tokens')).not_to be_checked
application_name_changed = "#{application_name} changed"
fill_in :doorkeeper_application_name, with: application_name_changed