Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue2
-rw-r--r--app/assets/javascripts/reports/accessibility_report/components/accessibility_issue_body.vue9
-rw-r--r--app/assets/stylesheets/framework/buttons.scss10
-rw-r--r--app/assets/stylesheets/framework/typography.scss8
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/helpers/groups_helper.rb2
-rw-r--r--app/views/discussions/_diff_with_notes.html.haml2
-rw-r--r--app/views/groups/settings/_general.html.haml16
-rw-r--r--app/views/shared/_group_form.html.haml22
-rw-r--r--config/feature_flags/development/rate_limit_frontend_requests.yml (renamed from config/feature_flags/development/github_importer_use_diff_note_with_suggestions.yml)12
-rw-r--r--db/post_migrate/20220127132200_cleanup_backfill_ci_namespace_mirrors.rb15
-rw-r--r--db/post_migrate/20220127132201_cleanup_backfill_ci_project_mirrors.rb15
-rw-r--r--db/schema_migrations/202201271322001
-rw-r--r--db/schema_migrations/202201271322011
-rw-r--r--doc/administration/gitaly/index.md6
-rw-r--r--doc/administration/gitaly/recovery.md2
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md2
-rw-r--r--doc/administration/pages/index.md4
-rw-r--r--doc/api/project_import_export.md2
-rw-r--r--doc/ci/cloud_services/google_cloud/index.md2
-rw-r--r--doc/ci/cloud_services/index.md2
-rw-r--r--doc/ci/runners/saas/macos_saas_runner.md2
-rw-r--r--doc/ci/runners/saas/windows_saas_runner.md6
-rw-r--r--doc/ci/variables/predefined_variables.md2
-rw-r--r--doc/development/contributing/design.md2
-rw-r--r--doc/development/documentation/styleguide/word_list.md4
-rw-r--r--doc/user/admin_area/analytics/dev_ops_report.md2
-rw-r--r--doc/user/admin_area/settings/user_and_ip_rate_limits.md4
-rw-r--r--doc/user/application_security/cluster_image_scanning/index.md2
-rw-r--r--doc/user/application_security/coverage_fuzzing/index.md2
-rw-r--r--doc/user/application_security/dast/browser_based.md2
-rw-r--r--doc/user/group/devops_adoption/index.md2
-rw-r--r--doc/user/group/index.md16
-rw-r--r--doc/user/packages/package_registry/index.md2
-rw-r--r--doc/user/profile/preferences.md2
-rw-r--r--doc/user/project/clusters/serverless/index.md2
-rw-r--r--doc/user/project/import/github.md2
-rw-r--r--doc/user/project/import/phabricator.md2
-rw-r--r--doc/user/project/merge_requests/commits.md2
-rw-r--r--doc/user/project/merge_requests/reviews/suggestions.md2
-rw-r--r--lib/gitlab/database/background_migration/batched_job.rb57
-rw-r--r--lib/gitlab/database/background_migration/batched_job_transition_log.rb6
-rw-r--r--lib/gitlab/database/background_migration/batched_migration.rb6
-rw-r--r--lib/gitlab/database/background_migration/batched_migration_runner.rb4
-rw-r--r--lib/gitlab/database/background_migration/batched_migration_wrapper.rb14
-rw-r--r--lib/gitlab/github_import/importer/diff_note_importer.rb13
-rw-r--r--lib/gitlab/rack_attack/request.rb19
-rw-r--r--locale/gitlab.pot68
-rw-r--r--spec/channels/application_cable/connection_spec.rb8
-rw-r--r--spec/factories/gitlab/database/background_migration/batched_jobs.rb16
-rw-r--r--spec/features/admin/admin_sees_background_migrations_spec.rb2
-rw-r--r--spec/features/groups_spec.rb6
-rw-r--r--spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js14
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_job_spec.rb94
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb27
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb27
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb9
-rw-r--r--spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb167
-rw-r--r--spec/lib/gitlab/rack_attack/request_spec.rb38
-rw-r--r--spec/requests/admin/background_migrations_controller_spec.rb2
-rw-r--r--spec/requests/api/commits_spec.rb10
-rw-r--r--spec/requests/rack_attack_global_spec.rb17
-rw-r--r--spec/support/helpers/rack_attack_spec_helpers.rb4
-rw-r--r--spec/support/helpers/session_helpers.rb16
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb85
66 files changed, 597 insertions, 340 deletions
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index b2d5910fd3f..b4f7ba5f960 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -107,7 +107,7 @@ export default {
<td v-if="error" class="js-error-lazy-load-diff diff-loading-error-block">
{{ __('Unable to load the diff') }}
<button
- class="btn-link btn-link-retry btn-no-padding js-toggle-lazy-diff-retry-button"
+ class="btn-link btn-link-retry gl-p-0 js-toggle-lazy-diff-retry-button"
@click="fetchDiff"
>
{{ __('Try again') }}
diff --git a/app/assets/javascripts/reports/accessibility_report/components/accessibility_issue_body.vue b/app/assets/javascripts/reports/accessibility_report/components/accessibility_issue_body.vue
index ed4f3c4e0fe..05ab5c2cc90 100644
--- a/app/assets/javascripts/reports/accessibility_report/components/accessibility_issue_body.vue
+++ b/app/assets/javascripts/reports/accessibility_report/components/accessibility_issue_body.vue
@@ -1,9 +1,10 @@
<script>
-import { GlLink } from '@gitlab/ui';
+import { GlBadge, GlLink } from '@gitlab/ui';
export default {
name: 'AccessibilityIssueBody',
components: {
+ GlBadge,
GlLink,
},
props: {
@@ -38,9 +39,9 @@ export default {
<template>
<div class="report-block-list-issue-description gl-mt-2 gl-mb-2">
<div ref="accessibility-issue-description" class="report-block-list-issue-description-text">
- <div v-if="isNew" ref="accessibility-issue-is-new-badge" class="badge badge-danger gl-mr-2">
- {{ s__('AccessibilityReport|New') }}
- </div>
+ <gl-badge v-if="isNew" class="gl-mr-2" variant="danger">{{
+ s__('AccessibilityReport|New')
+ }}</gl-badge>
<div>
{{
sprintf(
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index e0e9043ae24..1d7457e3c56 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -158,12 +158,6 @@
line-height: $gl-btn-small-line-height;
}
- &.btn-xs {
- padding: 2px $gl-btn-padding;
- font-size: $gl-btn-xs-font-size;
- line-height: $gl-btn-xs-line-height;
- }
-
&.btn-success {
@include btn-green;
}
@@ -438,10 +432,6 @@ fieldset[disabled] .btn,
cursor: default;
}
-.btn-no-padding {
- padding: 0;
-}
-
// This class helps convert `.gl-button` children so that they consistently
// match the style of `.btn` elements which might be around them. Ideally we
// wouldn't need this class.
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index b23b4e7d35d..feedc40b487 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -68,14 +68,6 @@
background-color: inherit;
}
- &:not(.md) img:not(.emoji) {
- border: 1px solid $white-normal;
- padding: 5px;
- margin: 5px 0;
- // Ensure that image does not exceed viewport
- max-height: calc(100vh - 100px);
- }
-
details {
margin-bottom: $gl-padding;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 21add43ad3f..f9b0f4f3118 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -589,8 +589,6 @@ $gl-btn-vert-padding: 8px;
$gl-btn-horz-padding: 12px;
$gl-btn-small-font-size: 13px;
$gl-btn-small-line-height: 18px;
-$gl-btn-xs-font-size: 13px;
-$gl-btn-xs-line-height: 13px;
/*
* Badges
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 3fbea0c0472..c58a365b884 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -204,7 +204,7 @@ module GroupsHelper
end
def group_url_error_message
- s_('GroupSettings|Please choose a group URL with no special characters or spaces.')
+ s_('GroupSettings|Choose a group path that does not start with a dash or end with a period. It can also contain alphanumeric characters and underscores.')
end
# Maps `jobs_to_be_done` values to option texts
diff --git a/app/views/discussions/_diff_with_notes.html.haml b/app/views/discussions/_diff_with_notes.html.haml
index b34b6f09662..cd0c9a016a5 100644
--- a/app/views/discussions/_diff_with_notes.html.haml
+++ b/app/views/discussions/_diff_with_notes.html.haml
@@ -29,7 +29,7 @@
%td.line_content.js-success-lazy-load
.js-code-placeholder
%td.js-error-lazy-load-diff.hidden.diff-loading-error-block
- - button = button_tag(_("Try again"), class: "btn-link gl-button btn-link-retry btn-no-padding js-toggle-lazy-diff-retry-button")
+ - button = button_tag(_("Try again"), class: "btn-link gl-button btn-link-retry gl-p-0 js-toggle-lazy-diff-retry-button")
= _("Unable to load the diff. %{button_try_again}").html_safe % { button_try_again: button}
= render "discussions/diff_discussion", discussions: [discussion], expanded: true
- else
diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml
index 0177f5a5563..2de3d01bcf8 100644
--- a/app/views/groups/settings/_general.html.haml
+++ b/app/views/groups/settings/_general.html.haml
@@ -5,18 +5,20 @@
%fieldset
.row
.form-group.col-md-5
- = f.label :name, _('Group name'), class: 'label-bold'
+ = f.label :name, s_('Groups|Group name'), class: 'label-bold'
= f.text_field :name, class: 'form-control', data: { qa_selector: 'group_name_field' }
+ .text-muted
+ = s_('Groups|Must start with letter, digit, emoji, or underscore. Can also contain periods, dashes, spaces, and parentheses.')
.form-group.col-md-7
- = f.label :id, _('Group ID'), class: 'label-bold'
+ = f.label :id, s_('Groups|Group ID'), class: 'label-bold'
= f.text_field :id, class: 'form-control w-auto', readonly: true
.row.gl-mt-3
.form-group.col-md-9
- = f.label :description, _('Group description'), class: 'label-bold'
+ = f.label :description, s_('Groups|Group description'), class: 'label-bold'
= f.text_area :description, class: 'form-control', rows: 3, maxlength: 250
- .form-text.text-muted= _('Optional.')
+ .form-text.text-muted= s_('Groups|Optional group description.')
= render 'shared/repository_size_limit_setting_registration_features_cta', form: f
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group
@@ -24,11 +26,11 @@
.form-group.gl-mt-3.gl-mb-6
.avatar-container.rect-avatar.s90
= group_icon(@group, alt: '', class: 'avatar group-avatar s90')
- = f.label :avatar, _('Group avatar'), class: 'label-bold d-block'
+ = f.label :avatar, s_('Groups|Group avatar'), class: 'label-bold d-block'
= render 'shared/choose_avatar_button', f: f
- if @group.avatar?
%hr
- = link_to _('Remove avatar'), group_avatar_path(@group.to_param), data: { confirm: _('Avatar will be removed. Are you sure?')}, method: :delete, class: 'gl-button btn btn-danger-secondary'
+ = link_to _('Groups|Remove avatar'), group_avatar_path(@group.to_param), data: { confirm: s_('Groups|Avatar will be removed. Are you sure?')}, method: :delete, class: 'gl-button btn btn-danger-secondary'
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
- = f.submit _('Save changes'), class: 'btn gl-button btn-confirm mt-4 js-dirty-submit', data: { qa_selector: 'save_name_visibility_settings_button' }
+ = f.submit s_('Groups|Save changes'), class: 'btn gl-button btn-confirm mt-4 js-dirty-submit', data: { qa_selector: 'save_name_visibility_settings_button' }
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index 63468340992..ee8cfe3abb6 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -5,16 +5,18 @@
.row
.form-group.group-name-holder.col-sm-12
= f.label :name, class: 'label-bold' do
- = _("Group name")
- = f.text_field :name, placeholder: _('My Awesome Group'), class: 'js-autofill-group-name form-control input-lg', data: { qa_selector: 'group_name_field' },
+ = s_('Groups|Group name')
+ = f.text_field :name, placeholder: _('My awesome group'), class: 'js-autofill-group-name form-control input-lg', data: { qa_selector: 'group_name_field' },
required: true,
- title: _('Please fill in a descriptive name for your group.'),
+ title: s_('Groups|Enter a descriptive name for your group.'),
autofocus: true
+ .text-muted
+ = s_('Groups|Must start with letter, digit, emoji, or underscore. Can also contain periods, dashes, spaces, and parentheses.')
.row
.form-group.col-xs-12.col-sm-8
= f.label :path, class: 'label-bold' do
- = _("Group URL")
+ = s_('Groups|Group URL')
.input-group.gl-field-error-anchor
.group-root-path.input-group-prepend.has-tooltip{ title: group_path, :'data-placement' => 'bottom' }
.input-group-text
@@ -29,21 +31,21 @@
maxlength: ::Namespace::URL_MAX_LENGTH,
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
%p.validation-error.gl-field-error.field-validation.hide
- = _("Group path is already taken. We've suggested one that is available.")
- %p.validation-success.gl-field-success.field-validation.hide= _('Group path is available.')
- %p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking group URL availability...')
+ = s_('Groups|Group path is unavailable. Path has been replaced with a suggested available path.')
+ %p.validation-success.gl-field-success.field-validation.hide= s_('Groups|Group path is available.')
+ %p.validation-pending.gl-field-error-ignore.field-validation.hide= s_('Groups|Checking group URL availability...')
- if @group.persisted?
.gl-alert.gl-alert-warning.gl-mt-3.gl-mb-3
= sprite_icon('warning', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
.gl-alert-body
- = _('Changing group URL can have unintended side effects.')
+ = s_('Groups|Changing group URL can have unintended side effects.')
= succeed '.' do
- = link_to _('Learn more'), help_page_path('user/group/index', anchor: 'change-a-groups-path'), target: '_blank', class: 'gl-link'
+ = link_to s_('Groups|Learn more'), help_page_path('user/group/index', anchor: 'change-a-groups-path'), target: '_blank', class: 'gl-link'
- if @group.persisted?
.row
.form-group.group-name-holder.col-sm-8
= f.label :id, class: 'label-bold' do
- = _("Group ID")
+ = s_('Groups|Group ID')
= f.text_field :id, class: 'form-control', readonly: true
diff --git a/config/feature_flags/development/github_importer_use_diff_note_with_suggestions.yml b/config/feature_flags/development/rate_limit_frontend_requests.yml
index c7f8d9f4943..42dfdb5182b 100644
--- a/config/feature_flags/development/github_importer_use_diff_note_with_suggestions.yml
+++ b/config/feature_flags/development/rate_limit_frontend_requests.yml
@@ -1,8 +1,8 @@
---
-name: github_importer_use_diff_note_with_suggestions
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71765
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344309
-milestone: '14.5'
+name: rate_limit_frontend_requests
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79341
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350623
+milestone: '14.8'
type: development
-group: group::import
-default_enabled: true
+group: group::integrations
+default_enabled: false
diff --git a/db/post_migrate/20220127132200_cleanup_backfill_ci_namespace_mirrors.rb b/db/post_migrate/20220127132200_cleanup_backfill_ci_namespace_mirrors.rb
new file mode 100644
index 00000000000..65ec2f9a282
--- /dev/null
+++ b/db/post_migrate/20220127132200_cleanup_backfill_ci_namespace_mirrors.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class CleanupBackfillCiNamespaceMirrors < Gitlab::Database::Migration[1.0]
+ MIGRATION = 'BackfillCiNamespaceMirrors'
+
+ disable_ddl_transaction!
+
+ def up
+ finalize_background_migration(MIGRATION)
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20220127132201_cleanup_backfill_ci_project_mirrors.rb b/db/post_migrate/20220127132201_cleanup_backfill_ci_project_mirrors.rb
new file mode 100644
index 00000000000..8c7d9a945ba
--- /dev/null
+++ b/db/post_migrate/20220127132201_cleanup_backfill_ci_project_mirrors.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class CleanupBackfillCiProjectMirrors < Gitlab::Database::Migration[1.0]
+ MIGRATION = 'BackfillCiProjectMirrors'
+
+ disable_ddl_transaction!
+
+ def up
+ finalize_background_migration(MIGRATION)
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20220127132200 b/db/schema_migrations/20220127132200
new file mode 100644
index 00000000000..8dc7ac4ddbd
--- /dev/null
+++ b/db/schema_migrations/20220127132200
@@ -0,0 +1 @@
+394f1fa34ccf9188f25102ac963829ebee07dddaf02f1d5958ec14d701fb6fe8 \ No newline at end of file
diff --git a/db/schema_migrations/20220127132201 b/db/schema_migrations/20220127132201
new file mode 100644
index 00000000000..f2c9cbbe38f
--- /dev/null
+++ b/db/schema_migrations/20220127132201
@@ -0,0 +1 @@
+f8ce7c183352ce08585eda83eb1e22c800b1b2044b93bc11858a74a8bd9a99d4 \ No newline at end of file
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 4d60832720b..3bb88c56934 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -290,7 +290,7 @@ including [horizontally distributing reads](https://gitlab.com/groups/gitlab-org
#### Distributed reads
-> - Introduced in GitLab 13.1 in [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga) with feature flag `gitaly_distributed_reads` set to disabled.
+> - Introduced in GitLab 13.1 in [beta](../../policy/alpha-beta-support.md#beta-features) with feature flag `gitaly_distributed_reads` set to disabled.
> - [Made generally available and enabled by default](https://gitlab.com/gitlab-org/gitaly/-/issues/2951) in GitLab 13.3.
> - [Disabled by default](https://gitlab.com/gitlab-org/gitaly/-/issues/3178) in GitLab 13.5.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitaly/-/issues/3334) in GitLab 13.8.
@@ -316,8 +316,8 @@ You can [monitor distribution of reads](#monitor-gitaly-cluster) using Prometheu
#### Strong consistency
-> - Introduced in GitLab 13.1 in [alpha](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga), disabled by default.
-> - Entered [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga) in GitLab 13.2, disabled by default.
+> - Introduced in GitLab 13.1 in [alpha](../../policy/alpha-beta-support.md#alpha-features), disabled by default.
+> - Entered [beta](../../policy/alpha-beta-support.md#beta-features) in GitLab 13.2, disabled by default.
> - In GitLab 13.3, disabled unless primary-wins voting strategy is disabled.
> - From GitLab 13.4, enabled by default.
> - From GitLab 13.5, you must use Git v2.28.0 or higher on Gitaly nodes to enable strong consistency.
diff --git a/doc/administration/gitaly/recovery.md b/doc/administration/gitaly/recovery.md
index e1b9a73908d..9210c8f08d3 100644
--- a/doc/administration/gitaly/recovery.md
+++ b/doc/administration/gitaly/recovery.md
@@ -28,7 +28,7 @@ To minimize data loss in GitLab 13.0 to 14.0, Gitaly Cluster:
### Read-only mode
-> - Introduced in GitLab 13.0 as [generally available](https://about.gitlab.com/handbook/product/gitlab-the-product/#generally-available-ga).
+> - Introduced in GitLab 13.0 as [generally available](../../policy/alpha-beta-support.md#generally-available-ga).
> - Between GitLab 13.0 and GitLab 13.2, read-only mode applied to the whole virtual storage and occurred whenever failover occurred.
> - [In GitLab 13.3 and later](https://gitlab.com/gitlab-org/gitaly/-/issues/2862), read-only mode applies on a per-repository basis and only occurs if a new primary is out of date.
new primary. If the failed primary contained unreplicated writes, [data loss can occur](#check-for-data-loss).
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index acb2df18e65..dca99879cc3 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -138,7 +138,7 @@ This is a brief overview. Please refer to the above instructions for more contex
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/299109) in GitLab 14.5.
WARNING:
-`gitlab-sshd` is in [**Alpha**](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga).
+`gitlab-sshd` is in [**Alpha**](../../policy/alpha-beta-support.md#alpha-features).
It is not ready for production use.
`gitlab-sshd` is [a standalone SSH server](https://gitlab.com/gitlab-org/gitlab-shell/-/tree/main/internal/sshd)
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 81465e0228c..efb080f5441 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -238,7 +238,7 @@ control over how the Pages daemon runs and serves content in your environment.
| `artifacts_server_url` | API URL to proxy artifact requests to. Defaults to GitLab `external URL` + `/api/v4`, for example `https://gitlab.com/api/v4`. When running a [separate Pages server](#running-gitlab-pages-on-a-separate-server), this URL must point to the main GitLab server's API. |
| `auth_redirect_uri` | Callback URL for authenticating with GitLab. Defaults to project's subdomain of `pages_external_url` + `/auth`. |
| `auth_secret` | Secret key for signing authentication requests. Leave blank to pull automatically from GitLab during OAuth registration. |
-| `client_cert_key_pairs` | Client certificates and keys used for mutual TLS with the GitLab API. See [Support mutual TLS when calling the GitLab API](#support-mutual-tls-when-calling-the-gitlab-api) for details. |
+| `client_cert_key_pairs` | Client certificates and keys used for mutual TLS with the GitLab API. See [Support mutual TLS when calling the GitLab API](#support-mutual-tls-when-calling-the-gitlab-api) for details. [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/548) in GitLab 14.8. |
| `dir` | Working directory for configuration and secrets files. |
| `enable` | Enable or disable GitLab Pages on the current system. |
| `external_http` | Configure Pages to bind to one or more secondary IP addresses, serving HTTP requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_http`. |
@@ -514,6 +514,8 @@ For Omnibus, this is fixed by [installing a custom CA in Omnibus GitLab](https:/
### Support mutual TLS when calling the GitLab API
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/548) in GitLab 14.8.
+
If GitLab has been [configured to require mutual TLS](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-2-way-ssl-client-authentication), you need to add the client certificates to Pages:
1. Configure in `/etc/gitlab/gitlab.rb`:
diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md
index 8e7607adf9b..6e9e9f0a81f 100644
--- a/doc/api/project_import_export.md
+++ b/doc/api/project_import_export.md
@@ -188,7 +188,7 @@ As an administrator, you can modify the maximum import file size. To do so, use
## Import a file from a remote object storage
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/282503) in GitLab 13.12 in [Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/282503) in GitLab 13.12 in [Beta](../policy/alpha-beta-support.md#beta-features).
This endpoint is behind a feature flag that is enabled by default.
diff --git a/doc/ci/cloud_services/google_cloud/index.md b/doc/ci/cloud_services/google_cloud/index.md
index aa48c971dc6..14928f91816 100644
--- a/doc/ci/cloud_services/google_cloud/index.md
+++ b/doc/ci/cloud_services/google_cloud/index.md
@@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Configure OpenID Connect with GCP Workload Identity Federation
WARNING:
-The `CI_JOB_JWT_V2` variable is under development [(alpha)](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha) and is not yet suitable for production use.
+The `CI_JOB_JWT_V2` variable is under development [(alpha)](../../../policy/alpha-beta-support.md#alpha-features) and is not yet suitable for production use.
This tutorial demonstrates authenticating to Google Cloud from a GitLab CI/CD job
using a JSON Web Token (JWT) token and Workload Identity Federation. This configuration
diff --git a/doc/ci/cloud_services/index.md b/doc/ci/cloud_services/index.md
index 43425333ce4..a0395730680 100644
--- a/doc/ci/cloud_services/index.md
+++ b/doc/ci/cloud_services/index.md
@@ -19,7 +19,7 @@ GitLab CI/CD supports [OpenID Connect (OIDC)](https://openid.net/connect/faq/) t
The original implementation of `CI_JOB_JWT` supports [HashiCorp Vault integration](../examples/authenticating-with-hashicorp-vault/). The updated implementation of `CI_JOB_JWT_V2` supports additional cloud providers with OIDC including AWS, GCP, and Vault.
WARNING:
-The `CI_JOB_JWT_V2` variable is under development [(alpha)](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha) and is not yet suitable for production use.
+The `CI_JOB_JWT_V2` variable is under development [(alpha)](../../policy/alpha-beta-support.md#alpha-features) and is not yet suitable for production use.
## Use cases
diff --git a/doc/ci/runners/saas/macos_saas_runner.md b/doc/ci/runners/saas/macos_saas_runner.md
index 40c4deb51aa..bfe408f4485 100644
--- a/doc/ci/runners/saas/macos_saas_runner.md
+++ b/doc/ci/runners/saas/macos_saas_runner.md
@@ -12,7 +12,7 @@ Use these runners to build, test, and deploy apps for the Apple ecosystem (macOS
of all the capabilities of the GitLab single DevOps platform and not have to manage or operate a
build environment.
-SaaS runners on macOS are in [Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta)
+SaaS runners on macOS are in [Beta](../../../policy/alpha-beta-support.md#beta-features)
and shouldn't be relied upon for mission-critical production jobs.
## Quickstart
diff --git a/doc/ci/runners/saas/windows_saas_runner.md b/doc/ci/runners/saas/windows_saas_runner.md
index 209c43e1e7f..dddb3afee7c 100644
--- a/doc/ci/runners/saas/windows_saas_runner.md
+++ b/doc/ci/runners/saas/windows_saas_runner.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# SaaS runners on Windows (beta) **(FREE SAAS)**
-SaaS runners on Windows are in [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta)
+SaaS runners on Windows are in [beta](../../../policy/alpha-beta-support.md#beta-features)
and shouldn't be used for production workloads.
During this beta period, the [shared runner quota for CI/CD minutes](../../pipelines/cicd_minutes.md)
@@ -22,7 +22,7 @@ Windows runners execute your CI/CD jobs on `n1-standard-2` instances with
the [package documentation](https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/gcp/windows-containers/blob/main/cookbooks/preinstalled-software/README.md).
We want to keep iterating to get Windows runners in a stable state and
-[generally available](https://about.gitlab.com/handbook/product/gitlab-the-product/#generally-available-ga).
+[generally available](../../../policy/alpha-beta-support.md#generally-available-ga).
You can follow our work towards this goal in the
[related epic](https://gitlab.com/groups/gitlab-org/-/epics/2162).
@@ -127,7 +127,7 @@ test:
## Limitations and known issues
- All the limitations mentioned in our [beta
- definition](https://about.gitlab.com/handbook/product/#beta).
+ definition](../../../policy/alpha-beta-support.md#beta-features).
- The average provisioning time for a new Windows VM is 5 minutes.
This means that you may notice slower build start times
on the Windows runner fleet during the beta. In a future
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 2a220908ee1..0f3461b3674 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -63,7 +63,7 @@ There are also a number of [variables you can use to configure runner behavior](
| `CI_JOB_IMAGE` | 12.9 | 12.9 | The name of the Docker image running the job. |
| `CI_JOB_JWT` | 12.10 | all | A RS256 JSON web token to authenticate with third party systems that support JWT authentication, for example [HashiCorp's Vault](../secrets/index.md). |
| `CI_JOB_JWT_V1` | 14.6 | all | The same value as `CI_JOB_JWT`. |
-| `CI_JOB_JWT_V2` | 14.6 | all | [**alpha:**](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga) A newly formatted RS256 JSON web token to increase compatibility. Similar to `CI_JOB_JWT`, except the issuer (`iss`) claim is changed from `gitlab.com` to `https://gitlab.com`, `sub` has changed from `job_id` to a string that contains the project path, and an `aud` claim is added. Format is subject to change. Be aware, the `aud` field is a constant value. Trusting JWTs in multiple relying parties can lead to [one RP sending a JWT to another one and acting maliciously as a job](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72555#note_769112331). |
+| `CI_JOB_JWT_V2` | 14.6 | all | [**alpha:**](../../policy/alpha-beta-support.md#alpha-features) A newly formatted RS256 JSON web token to increase compatibility. Similar to `CI_JOB_JWT`, except the issuer (`iss`) claim is changed from `gitlab.com` to `https://gitlab.com`, `sub` has changed from `job_id` to a string that contains the project path, and an `aud` claim is added. Format is subject to change. Be aware, the `aud` field is a constant value. Trusting JWTs in multiple relying parties can lead to [one RP sending a JWT to another one and acting maliciously as a job](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72555#note_769112331). |
| `CI_JOB_MANUAL` | 8.12 | all | `true` if a job was started manually. |
| `CI_JOB_NAME` | 9.0 | 0.5 | The name of the job. |
| `CI_JOB_STAGE` | 9.0 | 0.5 | The name of the job's stage. |
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
index e85f5dd8349..efa8d4b0c41 100644
--- a/doc/development/contributing/design.md
+++ b/doc/development/contributing/design.md
@@ -60,7 +60,7 @@ Check visual design properties using your browser's _elements inspector_ ([Chrom
guidelines.
- _Optionally_ consider [dark mode](../../user/profile/preferences.md#dark-mode). [^1]
- [^1]: You're not required to design for [dark mode](../../user/profile/preferences.md#dark-mode) while the feature is in [alpha](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha). The [UX Foundations team](https://about.gitlab.com/direction/ecosystem/foundations/) plans to improve the dark mode in the future. Until we integrate [Pajamas](https://design.gitlab.com/) components into the product and the underlying design strategy is in place to support dark mode, we cannot guarantee that we won't introduce bugs and debt to this mode. At your discretion, evaluate the need to create dark mode patches.
+ [^1]: You're not required to design for [dark mode](../../user/profile/preferences.md#dark-mode) while the feature is in [alpha](../../policy/alpha-beta-support.md#alpha-features). The [UX Foundations team](https://about.gitlab.com/direction/ecosystem/foundations/) plans to improve the dark mode in the future. Until we integrate [Pajamas](https://design.gitlab.com/) components into the product and the underlying design strategy is in place to support dark mode, we cannot guarantee that we won't introduce bugs and debt to this mode. At your discretion, evaluate the need to create dark mode patches.
### States
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index ff597f1ca8f..b5b05402918 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -98,7 +98,7 @@ This phrasing is more active and is from the user perspective, rather than the p
Use uppercase for **Alpha**. For example: **The XYZ feature is in Alpha.** or **This Alpha release is ready to test.**
-You might also want to link to [this section](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga)
+You might also want to link to [this section](../../../policy/alpha-beta-support.md#alpha-features)
in the handbook when writing about Alpha features.
## and/or
@@ -124,7 +124,7 @@ Try to avoid **below** when referring to an example or table in a documentation
Use uppercase for **Beta**. For example: **The XYZ feature is in Beta.** or **This Beta release is ready to test.**
-You might also want to link to [this section](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga)
+You might also want to link to [this section](../../../policy/alpha-beta-support.md#beta-features)
in the handbook when writing about Beta features.
## blacklist
diff --git a/doc/user/admin_area/analytics/dev_ops_report.md b/doc/user/admin_area/analytics/dev_ops_report.md
index df34cd03d71..2ad18d5f70e 100644
--- a/doc/user/admin_area/analytics/dev_ops_report.md
+++ b/doc/user/admin_area/analytics/dev_ops_report.md
@@ -39,7 +39,7 @@ feature is available.
## DevOps Adoption **(ULTIMATE SELF)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247112) in GitLab 13.7 as a [Beta feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta).
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247112) in GitLab 13.7 as a [Beta feature](../../../policy/alpha-beta-support.md#beta-features).
> - The Overview tab [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/330401) in GitLab 14.1.
> - DAST and SAST metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/328033) in GitLab 14.1.
> - Fuzz Testing metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/330398) in GitLab 14.2.
diff --git a/doc/user/admin_area/settings/user_and_ip_rate_limits.md b/doc/user/admin_area/settings/user_and_ip_rate_limits.md
index d713ef4b4e0..ad61f18343c 100644
--- a/doc/user/admin_area/settings/user_and_ip_rate_limits.md
+++ b/doc/user/admin_area/settings/user_and_ip_rate_limits.md
@@ -22,6 +22,10 @@ NOTE:
By default, all Git operations are first tried unauthenticated. Because of this, HTTP Git operations
may trigger the rate limits configured for unauthenticated requests.
+NOTE:
+The rate limits for API requests don't affect requests made by the frontend, as these are always
+counted as web traffic.
+
## Enable unauthenticated API request rate limit
To enable the unauthenticated request rate limit:
diff --git a/doc/user/application_security/cluster_image_scanning/index.md b/doc/user/application_security/cluster_image_scanning/index.md
index 5f2dd626526..e2ac9696d8a 100644
--- a/doc/user/application_security/cluster_image_scanning/index.md
+++ b/doc/user/application_security/cluster_image_scanning/index.md
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/) in GitLab 14.1.
WARNING:
-This analyzer is in [Alpha](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha)
+This analyzer is in [Alpha](../../../policy/alpha-beta-support.md#alpha-features)
and is unstable. The JSON report and CI/CD configuration may be subject to change or breakage
across GitLab releases.
diff --git a/doc/user/application_security/coverage_fuzzing/index.md b/doc/user/application_security/coverage_fuzzing/index.md
index 225c2ec0a4d..c3051f8a148 100644
--- a/doc/user/application_security/coverage_fuzzing/index.md
+++ b/doc/user/application_security/coverage_fuzzing/index.md
@@ -124,7 +124,7 @@ You can download the JSON report file from the CI/CD pipelines page. For more in
### Coverage-guided fuzz testing report
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220062) in GitLab 13.3 as an [Alpha feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220062) in GitLab 13.3 as an [Alpha feature](../../../policy/alpha-beta-support.md#alpha-features).
For detailed information about the `gl-coverage-fuzzing-report.json` file's format, read the
[schema](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/coverage-fuzzing-report-format.json).
diff --git a/doc/user/application_security/dast/browser_based.md b/doc/user/application_security/dast/browser_based.md
index 5d1e57553f4..4cde1847419 100644
--- a/doc/user/application_security/dast/browser_based.md
+++ b/doc/user/application_security/dast/browser_based.md
@@ -10,7 +10,7 @@ type: reference, howto
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323423) in GitLab 13.12.
WARNING:
-This product is in an early-access stage and is considered a [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta) feature.
+This product is in an early-access stage and is considered a [beta](../../../policy/alpha-beta-support.md#beta-features) feature.
GitLab DAST's new browser-based crawler is a crawl engine built by GitLab to test Single Page Applications (SPAs) and traditional web applications.
Due to the reliance of modern web applications on JavaScript, handling SPAs or applications that are dependent on JavaScript is paramount to ensuring proper coverage of an application for Dynamic Application Security Testing (DAST).
diff --git a/doc/user/group/devops_adoption/index.md b/doc/user/group/devops_adoption/index.md
index e570f40d8e6..333bef84dcc 100644
--- a/doc/user/group/devops_adoption/index.md
+++ b/doc/user/group/devops_adoption/index.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Group DevOps Adoption **(ULTIMATE)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321083) in GitLab 13.11 as a [Beta feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta).
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321083) in GitLab 13.11 as a [Beta feature](../../../policy/alpha-beta-support.md#beta-features).
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/333556) in GitLab 14.1.
> - The Overview tab [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/330401) in GitLab 14.1.
> - DAST and SAST metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/328033) in GitLab 14.1.
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 78a82ac3bd1..a36a0774a5d 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -68,19 +68,9 @@ To create a group:
- Select **Menu > Groups**, and on the right, select **Create group**.
- To the left of the search box, select the plus sign and then **New group**.
1. Select **Create group**.
-1. For the **Group name**, use only:
- - Alphanumeric characters
- - Emojis
- - Underscores
- - Dashes, dots, spaces, and parentheses (however, it cannot start with any of these characters)
-
- For a list of words that cannot be used as group names, see [reserved names](../reserved_names.md).
-
-1. For the **Group URL**, which is used for the [namespace](#namespaces),
- use only:
- - Alphanumeric characters
- - Underscores
- - Dashes and dots (it cannot start with dashes or end in a dot)
+1. Enter a name for the group in **Group name**. For a list of words that cannot be used as group names, see
+ [reserved names](../reserved_names.md).
+1. Enter a path for the group in **Group URL**, which is used for the [namespace](#namespaces).
1. Choose the [visibility level](../../public_access/public_access.md).
1. Personalize your GitLab experience by answering the following questions:
- What is your role?
diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md
index 55bfc56fc88..a2228148447 100644
--- a/doc/user/packages/package_registry/index.md
+++ b/doc/user/packages/package_registry/index.md
@@ -130,7 +130,7 @@ The Package Registry supports the following formats:
| [Go](../go_proxy/index.md) | 13.1+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3043) |
| [Ruby gems](../rubygems_registry/index.md) | 13.10+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3200) |
-[Status](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga):
+[Status](../../../policy/alpha-beta-support.md):
- Alpha: behind a feature flag and not officially supported.
- Beta: several known issues that may prevent expected use.
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index 63be88f90d6..52baf5189e1 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -43,7 +43,7 @@ The default theme is Indigo. You can choose between 10 themes:
GitLab has started work on dark mode! The dark mode Alpha release is available in the
spirit of iteration and the lower expectations of
-[Alpha versions](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha).
+[Alpha versions](../../policy/alpha-beta-support.md#alpha-features).
Progress on dark mode is tracked in the [Dark theme epic](https://gitlab.com/groups/gitlab-org/-/epics/2902).
See the epic for:
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index 265a60c6f2c..29164da307b 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Deprecated](https://gitlab.com/groups/gitlab-org/configure/-/epics/6) in GitLab 14.3.
WARNING:
-Serverless is currently in [alpha](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha).
+Serverless is currently in [alpha](../../../../policy/alpha-beta-support.md#alpha-features).
## Overview
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index ce105f3b29d..9f1c049045c 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -26,7 +26,7 @@ The following aspects of a project are imported:
- Regular issue and pull request comments
- [Git Large File Storage (LFS) Objects](../../../topics/git/lfs/index.md)
- Pull request comments replies in discussions ([GitLab.com & 14.5+](https://gitlab.com/gitlab-org/gitlab/-/issues/336596))
-- Diff Notes suggestions ([GitLab.com & 14.7+](https://gitlab.com/gitlab-org/gitlab/-/issues/340624)) [with a flag](../../../administration/feature_flags.md) named `github_importer_use_diff_note_with_suggestions`. Enabled by default.
+- Diff Notes suggestions ([GitLab.com & 14.7+](https://gitlab.com/gitlab-org/gitlab/-/issues/340624))
References to pull requests and issues are preserved (GitLab.com & 8.7+), and
each imported repository maintains visibility level unless that [visibility
diff --git a/doc/user/project/import/phabricator.md b/doc/user/project/import/phabricator.md
index 80f2d6d7e62..96b38b49960 100644
--- a/doc/user/project/import/phabricator.md
+++ b/doc/user/project/import/phabricator.md
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
WARNING:
The Phabricator task importer is in
-[beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta) and is
+[beta](../../../policy/alpha-beta-support.md#beta-features) and is
[**not** complete](https://gitlab.com/gitlab-org/gitlab/-/issues/284406). It imports
only an issue's title and description. The GitLab project created during the import
process contains only issues, and the associated repository is disabled.
diff --git a/doc/user/project/merge_requests/commits.md b/doc/user/project/merge_requests/commits.md
index 1c5c0a6854a..8970f958603 100644
--- a/doc/user/project/merge_requests/commits.md
+++ b/doc/user/project/merge_requests/commits.md
@@ -36,7 +36,7 @@ To seamlessly navigate among commits in a merge request:
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-viewing-merge-request-commits-in-context).
WARNING:
-This feature is in [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta)
+This feature is in [beta](../../../policy/alpha-beta-support.md#beta-features)
and is [incomplete](https://gitlab.com/groups/gitlab-org/-/epics/1192).
Previously merged commits can be added, but they can't be removed due to
[this bug](https://gitlab.com/gitlab-org/gitlab/-/issues/325538).
diff --git a/doc/user/project/merge_requests/reviews/suggestions.md b/doc/user/project/merge_requests/reviews/suggestions.md
index ce66d42a080..9868f2619ba 100644
--- a/doc/user/project/merge_requests/reviews/suggestions.md
+++ b/doc/user/project/merge_requests/reviews/suggestions.md
@@ -114,7 +114,7 @@ introduced by [#25381](https://gitlab.com/gitlab-org/gitlab/-/issues/25381).
## Batch suggestions
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/25486) in GitLab 13.1 as an [alpha feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha) with a flag named `batch_suggestions`, disabled by default.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/25486) in GitLab 13.1 as an [alpha feature](../../../../policy/alpha-beta-support.md#alpha-features) with a flag named `batch_suggestions`, disabled by default.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/227799) in GitLab 13.2.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/320755) in GitLab 13.11. [Feature flag `batch_suggestions`](https://gitlab.com/gitlab-org/gitlab/-/issues/320755) removed.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326168) custom commit messages for batch suggestions in GitLab 14.4.
diff --git a/lib/gitlab/database/background_migration/batched_job.rb b/lib/gitlab/database/background_migration/batched_job.rb
index e4db9391666..185b6d9629f 100644
--- a/lib/gitlab/database/background_migration/batched_job.rb
+++ b/lib/gitlab/database/background_migration/batched_job.rb
@@ -12,23 +12,58 @@ module Gitlab
MAX_ATTEMPTS = 3
STUCK_JOBS_TIMEOUT = 1.hour.freeze
- enum status: {
- pending: 0,
- running: 1,
- failed: 2,
- succeeded: 3
- }
-
belongs_to :batched_migration, foreign_key: :batched_background_migration_id
has_many :batched_job_transition_logs, foreign_key: :batched_background_migration_job_id
- scope :active, -> { where(status: [:pending, :running]) }
+ scope :active, -> { with_statuses(:pending, :running) }
scope :stuck, -> { active.where('updated_at <= ?', STUCK_JOBS_TIMEOUT.ago) }
- scope :retriable, -> { from_union([failed.where('attempts < ?', MAX_ATTEMPTS), self.stuck]) }
- scope :except_succeeded, -> { where(status: self.statuses.except(:succeeded).values) }
- scope :successful_in_execution_order, -> { where.not(finished_at: nil).succeeded.order(:finished_at) }
+ scope :retriable, -> { from_union([with_status(:failed).where('attempts < ?', MAX_ATTEMPTS), self.stuck]) }
+ scope :except_succeeded, -> { without_status(:succeeded) }
+ scope :successful_in_execution_order, -> { where.not(finished_at: nil).with_status(:succeeded).order(:finished_at) }
scope :with_preloads, -> { preload(:batched_migration) }
+ state_machine :status, initial: :pending do
+ state :pending, value: 0
+ state :running, value: 1
+ state :failed, value: 2
+ state :succeeded, value: 3
+
+ event :succeed do
+ transition any => :succeeded
+ end
+
+ event :failure do
+ transition any => :failed
+ end
+
+ event :run do
+ transition any => :running
+ end
+
+ before_transition any => [:failed, :succeeded] do |job|
+ job.finished_at = Time.current
+ end
+
+ before_transition any => :running do |job|
+ job.attempts += 1
+ job.started_at = Time.current
+ job.finished_at = nil
+ job.metrics = {}
+ end
+
+ after_transition do |job, transition|
+ error_hash = transition.args.find { |arg| arg[:error].present? }
+
+ exception = error_hash&.fetch(:error)
+
+ job.batched_job_transition_logs.create(previous_status: transition.from, next_status: transition.to, exception_class: exception&.class, exception_message: exception&.message)
+
+ Gitlab::ErrorTracking.track_exception(exception, batched_job_id: job.id) if exception
+
+ Gitlab::AppLogger.info(message: 'BatchedJob transition', batched_job_id: job.id, previous_state: transition.from_name, new_state: transition.to_name)
+ end
+ end
+
delegate :job_class, :table_name, :column_name, :job_arguments,
to: :batched_migration, prefix: :migration
diff --git a/lib/gitlab/database/background_migration/batched_job_transition_log.rb b/lib/gitlab/database/background_migration/batched_job_transition_log.rb
index 1d034107041..418bf1a101f 100644
--- a/lib/gitlab/database/background_migration/batched_job_transition_log.rb
+++ b/lib/gitlab/database/background_migration/batched_job_transition_log.rb
@@ -8,6 +8,8 @@ module Gitlab
self.table_name = :batched_background_migration_job_transition_logs
+ self.primary_key = :id
+
partitioned_by :created_at, strategy: :monthly, retain_for: 6.months
belongs_to :batched_job, foreign_key: :batched_background_migration_job_id
@@ -17,8 +19,8 @@ module Gitlab
validates :exception_class, length: { maximum: 100 }
validates :exception_message, length: { maximum: 1000 }
- enum previous_status: BatchedJob.statuses, _prefix: true
- enum next_status: BatchedJob.statuses, _prefix: true
+ enum previous_status: Gitlab::Database::BackgroundMigration::BatchedJob.state_machine.states.map(&:name), _prefix: true
+ enum next_status: Gitlab::Database::BackgroundMigration::BatchedJob.state_machine.states.map(&:name), _prefix: true
end
end
end
diff --git a/lib/gitlab/database/background_migration/batched_migration.rb b/lib/gitlab/database/background_migration/batched_migration.rb
index 2f066039874..1f8ca982ed5 100644
--- a/lib/gitlab/database/background_migration/batched_migration.rb
+++ b/lib/gitlab/database/background_migration/batched_migration.rb
@@ -47,7 +47,7 @@ module Gitlab
def self.successful_rows_counts(migrations)
BatchedJob
- .succeeded
+ .with_status(:succeeded)
.where(batched_background_migration_id: migrations)
.group(:batched_background_migration_id)
.sum(:batch_size)
@@ -71,7 +71,7 @@ module Gitlab
end
def retry_failed_jobs!
- batched_jobs.failed.each_batch(of: 100) do |batch|
+ batched_jobs.with_status(:failed).each_batch(of: 100) do |batch|
self.class.transaction do
batch.lock.each(&:split_and_retry!)
self.active!
@@ -102,7 +102,7 @@ module Gitlab
end
def migrated_tuple_count
- batched_jobs.succeeded.sum(:batch_size)
+ batched_jobs.with_status(:succeeded).sum(:batch_size)
end
def prometheus_labels
diff --git a/lib/gitlab/database/background_migration/batched_migration_runner.rb b/lib/gitlab/database/background_migration/batched_migration_runner.rb
index ea994cc09f6..9308bae20cf 100644
--- a/lib/gitlab/database/background_migration/batched_migration_runner.rb
+++ b/lib/gitlab/database/background_migration/batched_migration_runner.rb
@@ -67,7 +67,7 @@ module Gitlab
Gitlab::AppLogger.warn "Batched background migration for the given configuration is already finished: #{configuration}"
else
migration.finalizing!
- migration.batched_jobs.pending.each { |job| migration_wrapper.perform(job) }
+ migration.batched_jobs.with_status(:pending).each { |job| migration_wrapper.perform(job) }
run_migration_while(migration, :finalizing)
@@ -116,7 +116,7 @@ module Gitlab
def finish_active_migration(active_migration)
return if active_migration.batched_jobs.active.exists?
- if active_migration.batched_jobs.failed.exists?
+ if active_migration.batched_jobs.with_status(:failed).exists?
active_migration.failed!
else
active_migration.finished!
diff --git a/lib/gitlab/database/background_migration/batched_migration_wrapper.rb b/lib/gitlab/database/background_migration/batched_migration_wrapper.rb
index e37df102872..159d618451c 100644
--- a/lib/gitlab/database/background_migration/batched_migration_wrapper.rb
+++ b/lib/gitlab/database/background_migration/batched_migration_wrapper.rb
@@ -18,20 +18,19 @@ module Gitlab
execute_batch(batch_tracking_record)
- batch_tracking_record.status = :succeeded
- rescue Exception # rubocop:disable Lint/RescueException
- batch_tracking_record.status = :failed
+ batch_tracking_record.succeed!
+ rescue Exception => error # rubocop:disable Lint/RescueException
+ batch_tracking_record.failure!(error: error)
raise
ensure
- finish_tracking_execution(batch_tracking_record)
track_prometheus_metrics(batch_tracking_record)
end
private
def start_tracking_execution(tracking_record)
- tracking_record.update!(attempts: tracking_record.attempts + 1, status: :running, started_at: Time.current, finished_at: nil, metrics: {})
+ tracking_record.run!
end
def execute_batch(tracking_record)
@@ -51,11 +50,6 @@ module Gitlab
end
end
- def finish_tracking_execution(tracking_record)
- tracking_record.finished_at = Time.current
- tracking_record.save!
- end
-
def track_prometheus_metrics(tracking_record)
migration = tracking_record.batched_migration
base_labels = migration.prometheus_labels
diff --git a/lib/gitlab/github_import/importer/diff_note_importer.rb b/lib/gitlab/github_import/importer/diff_note_importer.rb
index 8a8d23401c1..02b582190b6 100644
--- a/lib/gitlab/github_import/importer/diff_note_importer.rb
+++ b/lib/gitlab/github_import/importer/diff_note_importer.rb
@@ -26,7 +26,7 @@ module Gitlab
# because it cannot use the BulkImporting strategy, which skips
# callbacks and validations. For this reason, notes that don't have
# suggestions are still imported with LegacyDiffNote
- if import_with_diff_note?
+ if note.contains_suggestion?
import_with_diff_note
else
import_with_legacy_diff_note
@@ -48,17 +48,6 @@ module Gitlab
attr_reader :note, :project, :client, :author_id, :author_found
- def import_with_diff_note?
- note.contains_suggestion? && use_diff_note_with_suggestions_enabled?
- end
-
- def use_diff_note_with_suggestions_enabled?
- Feature.enabled?(
- :github_importer_use_diff_note_with_suggestions,
- default_enabled: :yaml
- )
- end
-
def build_author_attributes
@author_id, @author_found = user_finder.author_id_for(note)
end
diff --git a/lib/gitlab/rack_attack/request.rb b/lib/gitlab/rack_attack/request.rb
index 7c8dc0be1c6..1e50df5f21c 100644
--- a/lib/gitlab/rack_attack/request.rb
+++ b/lib/gitlab/rack_attack/request.rb
@@ -3,6 +3,8 @@
module Gitlab
module RackAttack
module Request
+ include ::Gitlab::Utils::StrongMemoize
+
FILES_PATH_REGEX = %r{^/api/v\d+/projects/[^/]+/repository/files/.+}.freeze
GROUP_PATH_REGEX = %r{^/api/v\d+/groups/[^/]+/?$}.freeze
@@ -66,6 +68,7 @@ module Gitlab
def throttle_unauthenticated_api?
api_request? &&
!should_be_skipped? &&
+ !frontend_request? &&
!throttle_unauthenticated_packages_api? &&
!throttle_unauthenticated_files_api? &&
!throttle_unauthenticated_deprecated_api? &&
@@ -74,7 +77,7 @@ module Gitlab
end
def throttle_unauthenticated_web?
- web_request? &&
+ (web_request? || frontend_request?) &&
!should_be_skipped? &&
# TODO: Column will be renamed in https://gitlab.com/gitlab-org/gitlab/-/issues/340031
Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
@@ -83,6 +86,7 @@ module Gitlab
def throttle_authenticated_api?
api_request? &&
+ !frontend_request? &&
!throttle_authenticated_packages_api? &&
!throttle_authenticated_files_api? &&
!throttle_authenticated_deprecated_api? &&
@@ -90,7 +94,7 @@ module Gitlab
end
def throttle_authenticated_web?
- web_request? &&
+ (web_request? || frontend_request?) &&
!throttle_authenticated_git_lfs? &&
Gitlab::Throttle.settings.throttle_authenticated_web_enabled
end
@@ -185,6 +189,17 @@ module Gitlab
path.match?(FILES_PATH_REGEX)
end
+ def frontend_request?
+ return false unless Feature.enabled?(:rate_limit_frontend_requests, default_enabled: :yaml)
+
+ strong_memoize(:frontend_request) do
+ next false unless env.include?('HTTP_X_CSRF_TOKEN') && session.include?(:_csrf_token)
+
+ # CSRF tokens are not verified for GET/HEAD requests, so we pretend that we always have a POST request.
+ Gitlab::RequestForgeryProtection.verified?(env.merge('REQUEST_METHOD' => 'POST'))
+ end
+ end
+
def deprecated_api_request?
# The projects member of the groups endpoint is deprecated. If left
# unspecified, with_projects defaults to true
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 50d36aaf7a8..17b0264393a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6802,9 +6802,6 @@ msgstr ""
msgid "Changing any setting here requires an application restart"
msgstr ""
-msgid "Changing group URL can have unintended side effects."
-msgstr ""
-
msgid "Charts can't be displayed as the request for data has timed out. %{documentationLink}"
msgstr ""
@@ -6892,9 +6889,6 @@ msgstr ""
msgid "Checking branch availability..."
msgstr ""
-msgid "Checking group URL availability..."
-msgstr ""
-
msgid "Checking group path availability..."
msgstr ""
@@ -16841,9 +16835,6 @@ msgstr ""
msgid "Group Hooks"
msgstr ""
-msgid "Group ID"
-msgstr ""
-
msgid "Group Owner must have signed in with SAML before enabling Group Managed Accounts"
msgstr ""
@@ -16871,9 +16862,6 @@ msgstr ""
msgid "Group by"
msgstr ""
-msgid "Group description"
-msgstr ""
-
msgid "Group description (optional)"
msgstr ""
@@ -17288,6 +17276,9 @@ msgstr ""
msgid "GroupSettings|Changing a group's URL can have unintended side effects."
msgstr ""
+msgid "GroupSettings|Choose a group path that does not start with a dash or end with a period. It can also contain alphanumeric characters and underscores."
+msgstr ""
+
msgid "GroupSettings|Compliance frameworks"
msgstr ""
@@ -17570,6 +17561,54 @@ msgstr ""
msgid "GroupsTree|Search by name"
msgstr ""
+msgid "Groups|Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Groups|Changing group URL can have unintended side effects."
+msgstr ""
+
+msgid "Groups|Checking group URL availability..."
+msgstr ""
+
+msgid "Groups|Enter a descriptive name for your group."
+msgstr ""
+
+msgid "Groups|Group ID"
+msgstr ""
+
+msgid "Groups|Group URL"
+msgstr ""
+
+msgid "Groups|Group avatar"
+msgstr ""
+
+msgid "Groups|Group description"
+msgstr ""
+
+msgid "Groups|Group name"
+msgstr ""
+
+msgid "Groups|Group path is available."
+msgstr ""
+
+msgid "Groups|Group path is unavailable. Path has been replaced with a suggested available path."
+msgstr ""
+
+msgid "Groups|Learn more"
+msgstr ""
+
+msgid "Groups|Must start with letter, digit, emoji, or underscore. Can also contain periods, dashes, spaces, and parentheses."
+msgstr ""
+
+msgid "Groups|Optional group description."
+msgstr ""
+
+msgid "Groups|Remove avatar"
+msgstr ""
+
+msgid "Groups|Save changes"
+msgstr ""
+
msgid "Guideline"
msgstr ""
@@ -23449,7 +23488,7 @@ msgstr ""
msgid "Must match with the %{codeStart}geo_node_name%{codeEnd} in %{codeStart}/etc/gitlab/gitlab.rb%{codeEnd}. %{linkStart}Learn more%{linkEnd}"
msgstr ""
-msgid "My Awesome Group"
+msgid "My awesome group"
msgstr ""
msgid "My company or team"
@@ -25199,9 +25238,6 @@ msgstr ""
msgid "Optional parameter \"variables\" must be a Hash. Ex: variables[key1]=value1"
msgstr ""
-msgid "Optional."
-msgstr ""
-
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
diff --git a/spec/channels/application_cable/connection_spec.rb b/spec/channels/application_cable/connection_spec.rb
index c10e0c0cab4..affde0095cf 100644
--- a/spec/channels/application_cable/connection_spec.rb
+++ b/spec/channels/application_cable/connection_spec.rb
@@ -3,15 +3,11 @@
require 'spec_helper'
RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_sessions do
- let(:session_id) { Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') }
+ include SessionHelpers
context 'when session cookie is set' do
before do
- Gitlab::Redis::Sessions.with do |redis|
- redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
- end
-
- cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ stub_session(session_hash)
end
context 'when user is logged in' do
diff --git a/spec/factories/gitlab/database/background_migration/batched_jobs.rb b/spec/factories/gitlab/database/background_migration/batched_jobs.rb
index cec20616f7f..3c7dcd03701 100644
--- a/spec/factories/gitlab/database/background_migration/batched_jobs.rb
+++ b/spec/factories/gitlab/database/background_migration/batched_jobs.rb
@@ -9,5 +9,21 @@ FactoryBot.define do
batch_size { 5 }
sub_batch_size { 1 }
pause_ms { 100 }
+
+ trait(:pending) do
+ status { 0 }
+ end
+
+ trait(:running) do
+ status { 1 }
+ end
+
+ trait(:failed) do
+ status { 2 }
+ end
+
+ trait(:succeeded) do
+ status { 3 }
+ end
end
end
diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb
index e71cb11b413..a3d0c7bdd4d 100644
--- a/spec/features/admin/admin_sees_background_migrations_spec.rb
+++ b/spec/features/admin/admin_sees_background_migrations_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe "Admin > Admin sees background migrations" do
let_it_be(:finished_migration) { create(:batched_background_migration, table_name: 'finished', status: :finished) }
before_all do
- create(:batched_background_migration_job, batched_migration: failed_migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3)
+ create(:batched_background_migration_job, :failed, batched_migration: failed_migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3)
end
before do
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 19f60ce55d3..925bbc47cf6 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe 'Group' do
click_button 'Create group'
expect(current_path).to eq(new_group_path)
- expect(page).to have_text('Please choose a group URL with no special characters or spaces.')
+ expect(page).to have_text('Choose a group path that does not start with a dash or end with a period. It can also contain alphanumeric characters and underscores.')
end
end
@@ -90,7 +90,7 @@ RSpec.describe 'Group' do
fill_in 'group_path', with: user.username
wait_for_requests
- expect(page).to have_content("Group path is already taken. We've suggested one that is available.")
+ expect(page).to have_content("Group path is unavailable. Path has been replaced with a suggested available path.")
end
it 'does not break after an invalid form submit' do
@@ -279,7 +279,7 @@ RSpec.describe 'Group' do
fill_in 'Group URL', with: subgroup.path
wait_for_requests
- expect(page).to have_content("Group path is already taken. We've suggested one that is available.")
+ expect(page).to have_content("Group path is unavailable. Path has been replaced with a suggested available path.")
end
end
end
diff --git a/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js b/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js
index 794deca42ac..ddabb7194cb 100644
--- a/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js
+++ b/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js
@@ -1,3 +1,4 @@
+import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import AccessibilityIssueBody from '~/reports/accessibility_report/components/accessibility_issue_body.vue';
@@ -29,7 +30,7 @@ describe('CustomMetricsForm', () => {
});
};
- const findIsNewBadge = () => wrapper.find({ ref: 'accessibility-issue-is-new-badge' });
+ const findIsNewBadge = () => wrapper.findComponent(GlBadge);
beforeEach(() => {
mountComponent(issue);
@@ -37,7 +38,6 @@ describe('CustomMetricsForm', () => {
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
it('Displays the issue message', () => {
@@ -52,7 +52,7 @@ describe('CustomMetricsForm', () => {
.find({ ref: 'accessibility-issue-learn-more' })
.attributes('href');
- expect(learnMoreUrl).toEqual(issue.learnMoreUrl);
+ expect(learnMoreUrl).toBe(issue.learnMoreUrl);
});
});
@@ -69,7 +69,7 @@ describe('CustomMetricsForm', () => {
.find({ ref: 'accessibility-issue-learn-more' })
.attributes('href');
- expect(learnMoreUrl).toEqual('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
+ expect(learnMoreUrl).toBe('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
});
});
@@ -86,7 +86,7 @@ describe('CustomMetricsForm', () => {
.find({ ref: 'accessibility-issue-learn-more' })
.attributes('href');
- expect(learnMoreUrl).toEqual('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
+ expect(learnMoreUrl).toBe('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
});
});
@@ -96,7 +96,7 @@ describe('CustomMetricsForm', () => {
});
it('Renders the new badge', () => {
- expect(findIsNewBadge().exists()).toEqual(true);
+ expect(findIsNewBadge().exists()).toBe(true);
});
});
@@ -106,7 +106,7 @@ describe('CustomMetricsForm', () => {
});
it('Does not render the new badge', () => {
- expect(findIsNewBadge().exists()).toEqual(false);
+ expect(findIsNewBadge().exists()).toBe(false);
});
});
});
diff --git a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
index d810cd2b570..7338ea657b9 100644
--- a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
@@ -10,16 +10,82 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
it { is_expected.to have_many(:batched_job_transition_logs).with_foreign_key(:batched_background_migration_job_id) }
end
+ describe 'state machine' do
+ let_it_be(:job) { create(:batched_background_migration_job, :failed) }
+
+ context 'when a job is running' do
+ it 'logs the transition' do
+ expect(Gitlab::AppLogger).to receive(:info).with( { batched_job_id: job.id, message: 'BatchedJob transition', new_state: :running, previous_state: :failed } )
+
+ expect { job.run! }.to change(job, :started_at)
+ end
+ end
+
+ context 'when a job succeed' do
+ let(:job) { create(:batched_background_migration_job, :running) }
+
+ it 'logs the transition' do
+ expect(Gitlab::AppLogger).to receive(:info).with( { batched_job_id: job.id, message: 'BatchedJob transition', new_state: :succeeded, previous_state: :running } )
+
+ job.succeed!
+ end
+
+ it 'updates the finished_at' do
+ expect { job.succeed! }.to change(job, :finished_at).from(nil).to(Time)
+ end
+
+ it 'creates a new transition log' do
+ job.succeed!
+
+ transition_log = job.batched_job_transition_logs.first
+
+ expect(transition_log.next_status).to eq('succeeded')
+ expect(transition_log.exception_class).to be_nil
+ expect(transition_log.exception_message).to be_nil
+ end
+ end
+
+ context 'when a job fails' do
+ let(:job) { create(:batched_background_migration_job, :running) }
+
+ it 'logs the transition' do
+ expect(Gitlab::AppLogger).to receive(:info).with( { batched_job_id: job.id, message: 'BatchedJob transition', new_state: :failed, previous_state: :running } )
+
+ job.failure!
+ end
+
+ it 'tracks the exception' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(RuntimeError, { batched_job_id: job.id } )
+
+ job.failure!(error: RuntimeError.new)
+ end
+
+ it 'updates the finished_at' do
+ expect { job.failure! }.to change(job, :finished_at).from(nil).to(Time)
+ end
+
+ it 'creates a new transition log' do
+ job.failure!(error: RuntimeError.new)
+
+ transition_log = job.batched_job_transition_logs.first
+
+ expect(transition_log.next_status).to eq('failed')
+ expect(transition_log.exception_class).to eq('RuntimeError')
+ expect(transition_log.exception_message).to eq('RuntimeError')
+ end
+ end
+ end
+
describe 'scopes' do
let_it_be(:fixed_time) { Time.new(2021, 04, 27, 10, 00, 00, 00) }
- let_it_be(:pending_job) { create(:batched_background_migration_job, status: :pending, updated_at: fixed_time) }
- let_it_be(:running_job) { create(:batched_background_migration_job, status: :running, updated_at: fixed_time) }
- let_it_be(:stuck_job) { create(:batched_background_migration_job, status: :pending, updated_at: fixed_time - described_class::STUCK_JOBS_TIMEOUT) }
- let_it_be(:failed_job) { create(:batched_background_migration_job, status: :failed, attempts: 1) }
+ let_it_be(:pending_job) { create(:batched_background_migration_job, :pending, updated_at: fixed_time) }
+ let_it_be(:running_job) { create(:batched_background_migration_job, :running, updated_at: fixed_time) }
+ let_it_be(:stuck_job) { create(:batched_background_migration_job, :pending, updated_at: fixed_time - described_class::STUCK_JOBS_TIMEOUT) }
+ let_it_be(:failed_job) { create(:batched_background_migration_job, :failed, attempts: 1) }
- let!(:max_attempts_failed_job) { create(:batched_background_migration_job, status: :failed, attempts: described_class::MAX_ATTEMPTS) }
- let!(:succeeded_job) { create(:batched_background_migration_job, status: :succeeded) }
+ let!(:max_attempts_failed_job) { create(:batched_background_migration_job, :failed, attempts: described_class::MAX_ATTEMPTS) }
+ let!(:succeeded_job) { create(:batched_background_migration_job, :succeeded) }
before do
travel_to fixed_time
@@ -83,10 +149,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
subject { job.time_efficiency }
let(:migration) { build(:batched_background_migration, interval: 120.seconds) }
- let(:job) { build(:batched_background_migration_job, status: :succeeded, batched_migration: migration) }
+ let(:job) { build(:batched_background_migration_job, :succeeded, batched_migration: migration) }
context 'when job has not yet succeeded' do
- let(:job) { build(:batched_background_migration_job, status: :running) }
+ let(:job) { build(:batched_background_migration_job, :running) }
it 'returns nil' do
expect(subject).to be_nil
@@ -131,7 +197,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
describe '#split_and_retry!' do
- let!(:job) { create(:batched_background_migration_job, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3) }
+ let!(:job) { create(:batched_background_migration_job, :failed, batch_size: 10, min_value: 6, max_value: 15, attempts: 3) }
context 'when job can be split' do
before do
@@ -147,7 +213,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
min_value: 6,
max_value: 10,
batch_size: 5,
- status: 'failed',
+ status_name: :failed,
attempts: 0,
started_at: nil,
finished_at: nil,
@@ -161,7 +227,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
min_value: 11,
max_value: 15,
batch_size: 5,
- status: 'failed',
+ status_name: :failed,
attempts: 0,
started_at: nil,
finished_at: nil,
@@ -178,7 +244,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
context 'when job is not failed' do
- let!(:job) { create(:batched_background_migration_job, status: :succeeded) }
+ let!(:job) { create(:batched_background_migration_job, :succeeded) }
it 'raises an exception' do
expect { job.split_and_retry! }.to raise_error 'Only failed jobs can be split'
@@ -186,7 +252,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
context 'when batch size is already 1' do
- let!(:job) { create(:batched_background_migration_job, batch_size: 1, status: :failed) }
+ let!(:job) { create(:batched_background_migration_job, :failed, batch_size: 1) }
it 'raises an exception' do
expect { job.split_and_retry! }.to raise_error 'Job cannot be split further'
@@ -205,7 +271,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
expect(job.batch_size).to eq(5)
expect(job.attempts).to eq(0)
- expect(job.status).to eq('failed')
+ expect(job.status_name).to eq(:failed)
end
end
end
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
index 04c18a98ee6..bb2c6b9a3ae 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
@@ -96,13 +96,12 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
end
let!(:previous_job) do
- create(:batched_background_migration_job,
+ create(:batched_background_migration_job, :succeeded,
batched_migration: migration,
min_value: event1.id,
max_value: event2.id,
batch_size: 2,
- sub_batch_size: 1,
- status: :succeeded
+ sub_batch_size: 1
)
end
@@ -144,7 +143,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration has failed jobs' do
before do
- previous_job.update!(status: :failed)
+ previous_job.failure!
end
it 'retries the failed job' do
@@ -172,7 +171,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration has stuck jobs' do
before do
- previous_job.update!(status: :running, updated_at: 1.hour.ago - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
+ previous_job.update!(status_event: 'run', updated_at: 1.hour.ago - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
end
it 'retries the stuck job' do
@@ -186,7 +185,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration has possible stuck jobs' do
before do
- previous_job.update!(status: :running, updated_at: 1.hour.from_now - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
+ previous_job.update!(status_event: 'run', updated_at: 1.hour.from_now - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
end
it 'keeps the migration active' do
@@ -201,13 +200,13 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when the migration has batches to process and failed jobs' do
before do
migration.update!(max_value: event3.id)
- previous_job.update!(status: :failed)
+ previous_job.failure!
end
it 'runs next batch then retries the failed job' do
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.last)
- job_record.update!(status: :succeeded)
+ job_record.succeed!
end
expect { runner.run_migration_job(migration) }.to change { job_relation.count }.by(1)
@@ -264,12 +263,12 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
it 'runs all jobs inline until finishing the migration' do
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.first)
- job_record.update!(status: :succeeded)
+ job_record.succeed!
end
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.last)
- job_record.update!(status: :succeeded)
+ job_record.succeed!
end
expect { runner.run_entire_migration(migration) }.to change { job_relation.count }.by(2)
@@ -330,9 +329,9 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
pause_ms: 0
}
- create(:batched_background_migration_job, common_attributes.merge(status: :succeeded, min_value: 1, max_value: 2))
- create(:batched_background_migration_job, common_attributes.merge(status: :pending, min_value: 3, max_value: 4))
- create(:batched_background_migration_job, common_attributes.merge(status: :failed, min_value: 5, max_value: 6, attempts: 1))
+ create(:batched_background_migration_job, :succeeded, common_attributes.merge(min_value: 1, max_value: 2))
+ create(:batched_background_migration_job, :pending, common_attributes.merge(min_value: 3, max_value: 4))
+ create(:batched_background_migration_job, :failed, common_attributes.merge(min_value: 5, max_value: 6, attempts: 1))
end
it 'completes the migration' do
@@ -359,7 +358,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration fails to complete' do
it 'raises an error' do
- batched_migration.batched_jobs.failed.update_all(attempts: Gitlab::Database::BackgroundMigration::BatchedJob::MAX_ATTEMPTS)
+ batched_migration.batched_jobs.with_status(:failed).update_all(attempts: Gitlab::Database::BackgroundMigration::BatchedJob::MAX_ATTEMPTS)
expect do
runner.finalize(
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
index 01d61a525e6..ea4ba4dd137 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
context 'when there are failed jobs' do
let(:batched_migration) { create(:batched_background_migration, status: :active, total_tuple_count: 100) }
- let!(:batched_job) { create(:batched_background_migration_job, batched_migration: batched_migration, status: :failed) }
+ let!(:batched_job) { create(:batched_background_migration_job, :failed, batched_migration: batched_migration) }
it 'raises an exception' do
expect { batched_migration.finished! }.to raise_error(ActiveRecord::RecordInvalid)
@@ -37,7 +37,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
context 'when the jobs are completed' do
let(:batched_migration) { create(:batched_background_migration, status: :active, total_tuple_count: 100) }
- let!(:batched_job) { create(:batched_background_migration_job, batched_migration: batched_migration, status: :succeeded) }
+ let!(:batched_job) { create(:batched_background_migration_job, :succeeded, batched_migration: batched_migration) }
it 'finishes the migration' do
batched_migration.finished!
@@ -64,7 +64,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
it 'returns the first active migration according to queue order' do
expect(described_class.active_migration).to eq(migration2)
- create(:batched_background_migration_job, batched_migration: migration1, batch_size: 1000, status: :succeeded)
+ create(:batched_background_migration_job, :succeeded, batched_migration: migration1, batch_size: 1000)
end
end
@@ -84,10 +84,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
let!(:migration_without_jobs) { create(:batched_background_migration) }
before do
- create(:batched_background_migration_job, batched_migration: migration1, batch_size: 1000, status: :succeeded)
- create(:batched_background_migration_job, batched_migration: migration1, batch_size: 200, status: :failed)
- create(:batched_background_migration_job, batched_migration: migration2, batch_size: 500, status: :succeeded)
- create(:batched_background_migration_job, batched_migration: migration2, batch_size: 200, status: :running)
+ create(:batched_background_migration_job, :succeeded, batched_migration: migration1, batch_size: 1000)
+ create(:batched_background_migration_job, :failed, batched_migration: migration1, batch_size: 200)
+ create(:batched_background_migration_job, :succeeded, batched_migration: migration2, batch_size: 500)
+ create(:batched_background_migration_job, :running, batched_migration: migration2, batch_size: 200)
end
it 'returns totals from successful jobs' do
@@ -268,7 +268,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject(:retry_failed_jobs) { batched_migration.retry_failed_jobs! }
context 'when there are failed migration jobs' do
- let!(:batched_background_migration_job) { create(:batched_background_migration_job, batched_migration: batched_migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3) }
+ let!(:batched_background_migration_job) { create(:batched_background_migration_job, :failed, batched_migration: batched_migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3) }
before do
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
@@ -312,9 +312,9 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
let(:batched_migration) { create(:batched_background_migration) }
before do
- create_list(:batched_background_migration_job, 5, status: :succeeded, batch_size: 1_000, batched_migration: batched_migration)
- create_list(:batched_background_migration_job, 1, status: :running, batch_size: 1_000, batched_migration: batched_migration)
- create_list(:batched_background_migration_job, 1, status: :failed, batch_size: 1_000, batched_migration: batched_migration)
+ create_list(:batched_background_migration_job, 5, :succeeded, batch_size: 1_000, batched_migration: batched_migration)
+ create_list(:batched_background_migration_job, 1, :running, batch_size: 1_000, batched_migration: batched_migration)
+ create_list(:batched_background_migration_job, 1, :failed, batch_size: 1_000, batched_migration: batched_migration)
end
it 'sums the batch_size of succeeded jobs' do
@@ -347,7 +347,6 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
let_it_be(:common_attrs) do
{
- status: :succeeded,
batched_migration: migration,
finished_at: end_time
}
@@ -357,7 +356,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject { migration.smoothed_time_efficiency(number_of_jobs: 10) }
it 'returns nil' do
- create_list(:batched_background_migration_job, 9, **common_attrs)
+ create_list(:batched_background_migration_job, 9, :succeeded, **common_attrs)
expect(subject).to be_nil
end
@@ -369,6 +368,8 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject { migration.smoothed_time_efficiency(number_of_jobs: number_of_jobs) }
+ let!(:jobs) { create_list(:batched_background_migration_job, number_of_jobs, :succeeded, **common_attrs.merge(batched_migration: migration)) }
+
before do
expect(migration).to receive_message_chain(:batched_jobs, :successful_in_execution_order, :reverse_order, :limit, :with_preloads)
.and_return(jobs)
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
index c1183a15e37..b3c4522a2a1 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
@@ -35,8 +35,6 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
expect(job_instance).to receive(:perform)
expect(job_instance).to receive(:batch_metrics).and_return(test_metrics)
- expect(job_record).to receive(:update!).with(hash_including(attempts: 1, status: :running)).and_call_original
-
freeze_time do
subject
@@ -51,11 +49,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
context 'when running a job that failed previously' do
let!(:job_record) do
- create(:batched_background_migration_job,
+ create(:batched_background_migration_job, :failed,
batched_migration: active_migration,
pause_ms: pause_ms,
attempts: 1,
- status: :failed,
finished_at: 1.hour.ago,
metrics: { 'my_metrics' => 'some_value' }
)
@@ -67,10 +64,6 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
expect(job_instance).to receive(:perform)
expect(job_instance).to receive(:batch_metrics).and_return(updated_metrics)
- expect(job_record).to receive(:update!).with(
- hash_including(attempts: 2, status: :running, finished_at: nil, metrics: {})
- ).and_call_original
-
freeze_time do
subject
diff --git a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
index a0e78186caa..c8e744ab262 100644
--- a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
@@ -119,123 +119,80 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
.and_return(discussion_id)
end
- context 'when github_importer_use_diff_note_with_suggestions is disabled' do
- before do
- stub_feature_flags(github_importer_use_diff_note_with_suggestions: false)
+ it_behaves_like 'diff notes without suggestion'
+
+ context 'when the note has suggestions' do
+ let(:note_body) do
+ <<~EOB
+ Suggestion:
+ ```suggestion
+ what do you think to do it like this
+ ```
+ EOB
end
- it_behaves_like 'diff notes without suggestion'
+ before do
+ stub_user_finder(user.id, true)
+ end
- context 'when the note has suggestions' do
- let(:note_body) do
- <<~EOB
+ it 'imports the note as diff note' do
+ expect { subject.execute }
+ .to change(DiffNote, :count)
+ .by(1)
+
+ note = project.notes.diff_notes.take
+ expect(note).to be_valid
+ expect(note.noteable_type).to eq('MergeRequest')
+ expect(note.noteable_id).to eq(merge_request.id)
+ expect(note.project_id).to eq(project.id)
+ expect(note.author_id).to eq(user.id)
+ expect(note.system).to eq(false)
+ expect(note.discussion_id).to eq(discussion_id)
+ expect(note.commit_id).to eq('original123abc')
+ expect(note.line_code).to eq(note_representation.line_code)
+ expect(note.type).to eq('DiffNote')
+ expect(note.created_at).to eq(created_at)
+ expect(note.updated_at).to eq(updated_at)
+ expect(note.position.to_h).to eq({
+ base_sha: merge_request.diffs.diff_refs.base_sha,
+ head_sha: merge_request.diffs.diff_refs.head_sha,
+ start_sha: merge_request.diffs.diff_refs.start_sha,
+ new_line: 15,
+ old_line: nil,
+ new_path: file_path,
+ old_path: file_path,
+ position_type: 'text',
+ line_range: nil
+ })
+ expect(note.note)
+ .to eq <<~NOTE
Suggestion:
- ```suggestion
+ ```suggestion:-0+0
what do you think to do it like this
```
- EOB
- end
-
- it 'imports the note' do
- stub_user_finder(user.id, true)
-
- expect { subject.execute }
- .to change(LegacyDiffNote, :count)
- .and not_change(DiffNote, :count)
-
- note = project.notes.diff_notes.take
- expect(note).to be_valid
- expect(note.note)
- .to eq <<~NOTE
- Suggestion:
- ```suggestion:-0+0
- what do you think to do it like this
- ```
- NOTE
- end
- end
- end
-
- context 'when github_importer_use_diff_note_with_suggestions is enabled' do
- before do
- stub_feature_flags(github_importer_use_diff_note_with_suggestions: true)
+ NOTE
end
- it_behaves_like 'diff notes without suggestion'
+ context 'when the note diff file creation fails' do
+ it 'falls back to the LegacyDiffNote' do
+ exception = ::DiffNote::NoteDiffFileCreationError.new('Failed to create diff note file')
- context 'when the note has suggestions' do
- let(:note_body) do
- <<~EOB
- Suggestion:
- ```suggestion
- what do you think to do it like this
- ```
- EOB
- end
+ expect_next_instance_of(::Import::Github::Notes::CreateService) do |service|
+ expect(service)
+ .to receive(:execute)
+ .and_raise(exception)
+ end
- before do
- stub_user_finder(user.id, true)
- end
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:warn)
+ .with(
+ message: 'Failed to create diff note file',
+ 'error.class': 'DiffNote::NoteDiffFileCreationError'
+ )
- it 'imports the note as diff note' do
expect { subject.execute }
- .to change(DiffNote, :count)
- .by(1)
-
- note = project.notes.diff_notes.take
- expect(note).to be_valid
- expect(note.noteable_type).to eq('MergeRequest')
- expect(note.noteable_id).to eq(merge_request.id)
- expect(note.project_id).to eq(project.id)
- expect(note.author_id).to eq(user.id)
- expect(note.system).to eq(false)
- expect(note.discussion_id).to eq(discussion_id)
- expect(note.commit_id).to eq('original123abc')
- expect(note.line_code).to eq(note_representation.line_code)
- expect(note.type).to eq('DiffNote')
- expect(note.created_at).to eq(created_at)
- expect(note.updated_at).to eq(updated_at)
- expect(note.position.to_h).to eq({
- base_sha: merge_request.diffs.diff_refs.base_sha,
- head_sha: merge_request.diffs.diff_refs.head_sha,
- start_sha: merge_request.diffs.diff_refs.start_sha,
- new_line: 15,
- old_line: nil,
- new_path: file_path,
- old_path: file_path,
- position_type: 'text',
- line_range: nil
- })
- expect(note.note)
- .to eq <<~NOTE
- Suggestion:
- ```suggestion:-0+0
- what do you think to do it like this
- ```
- NOTE
- end
-
- context 'when the note diff file creation fails' do
- it 'falls back to the LegacyDiffNote' do
- exception = ::DiffNote::NoteDiffFileCreationError.new('Failed to create diff note file')
-
- expect_next_instance_of(::Import::Github::Notes::CreateService) do |service|
- expect(service)
- .to receive(:execute)
- .and_raise(exception)
- end
-
- expect(Gitlab::GithubImport::Logger)
- .to receive(:warn)
- .with(
- message: 'Failed to create diff note file',
- 'error.class': 'DiffNote::NoteDiffFileCreationError'
- )
-
- expect { subject.execute }
- .to change(LegacyDiffNote, :count)
- .and not_change(DiffNote, :count)
- end
+ .to change(LegacyDiffNote, :count)
+ .and not_change(DiffNote, :count)
end
end
end
diff --git a/spec/lib/gitlab/rack_attack/request_spec.rb b/spec/lib/gitlab/rack_attack/request_spec.rb
index 37db588ce16..9b882f26480 100644
--- a/spec/lib/gitlab/rack_attack/request_spec.rb
+++ b/spec/lib/gitlab/rack_attack/request_spec.rb
@@ -192,6 +192,44 @@ RSpec.describe Gitlab::RackAttack::Request do
end
end
+ describe '#frontend_request?', :allow_forgery_protection do
+ subject { request.send(:frontend_request?) }
+
+ let(:path) { '/' }
+
+ # Define these as local variables so we can use them in the `where` block.
+ valid_token = SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH)
+ other_token = SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH)
+
+ where(:session, :env, :expected) do
+ {} | {} | false # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ {} | { 'HTTP_X_CSRF_TOKEN' => valid_token } | false
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => other_token } | false
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => valid_token } | true
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(rate_limit_frontend_requests: false)
+ end
+
+ where(:session, :env) do
+ {} | {} # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ {} | { 'HTTP_X_CSRF_TOKEN' => valid_token }
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => other_token }
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => valid_token }
+ end
+
+ with_them do
+ it { is_expected.to be(false) }
+ end
+ end
+ end
+
describe '#deprecated_api_request?' do
subject { request.send(:deprecated_api_request?) }
diff --git a/spec/requests/admin/background_migrations_controller_spec.rb b/spec/requests/admin/background_migrations_controller_spec.rb
index c7d5d5cae08..67c9c4df827 100644
--- a/spec/requests/admin/background_migrations_controller_spec.rb
+++ b/spec/requests/admin/background_migrations_controller_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Admin::BackgroundMigrationsController, :enable_admin_mode do
let(:migration) { create(:batched_background_migration, status: 'failed') }
before do
- create(:batched_background_migration_job, batched_migration: migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3)
+ create(:batched_background_migration_job, :failed, batched_migration: migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3)
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 27572b38451..156a4cf5ff3 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -5,6 +5,7 @@ require 'mime/types'
RSpec.describe API::Commits do
include ProjectForksHelper
+ include SessionHelpers
let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
@@ -384,14 +385,7 @@ RSpec.describe API::Commits do
context 'when using warden' do
it 'increments usage counters', :clean_gitlab_redis_sessions do
- session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
- session_hash = { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] }
-
- Gitlab::Redis::Sessions.with do |redis|
- redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
- end
-
- cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ stub_session('warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]])
expect(::Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_commits_count)
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb
index 25eae57a134..f2126e3cf9c 100644
--- a/spec/requests/rack_attack_global_spec.rb
+++ b/spec/requests/rack_attack_global_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_caching do
include RackAttackSpecHelpers
+ include SessionHelpers
let(:settings) { Gitlab::CurrentSettings.current_application_settings }
@@ -63,6 +64,22 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac
end
end
+ describe 'API requests from the frontend', :api, :clean_gitlab_redis_sessions do
+ context 'when unauthenticated' do
+ it_behaves_like 'rate-limited frontend API requests' do
+ let(:throttle_setting_prefix) { 'throttle_unauthenticated' }
+ end
+ end
+
+ context 'when authenticated' do
+ it_behaves_like 'rate-limited frontend API requests' do
+ let_it_be(:personal_access_token) { create(:personal_access_token) }
+
+ let(:throttle_setting_prefix) { 'throttle_authenticated' }
+ end
+ end
+ end
+
describe 'API requests authenticated with personal access token', :api do
let_it_be(:user) { create(:user) }
let_it_be(:token) { create(:personal_access_token, user: user) }
diff --git a/spec/support/helpers/rack_attack_spec_helpers.rb b/spec/support/helpers/rack_attack_spec_helpers.rb
index d50a6382a40..c82a87dc58e 100644
--- a/spec/support/helpers/rack_attack_spec_helpers.rb
+++ b/spec/support/helpers/rack_attack_spec_helpers.rb
@@ -26,14 +26,14 @@ module RackAttackSpecHelpers
{ 'AUTHORIZATION' => "Basic #{encoded_login}" }
end
- def expect_rejection(&block)
+ def expect_rejection(name = nil, &block)
yield
expect(response).to have_gitlab_http_status(:too_many_requests)
expect(response.headers.to_h).to include(
'RateLimit-Limit' => a_string_matching(/^\d+$/),
- 'RateLimit-Name' => a_string_matching(/^throttle_.*$/),
+ 'RateLimit-Name' => name || a_string_matching(/^throttle_.*$/),
'RateLimit-Observed' => a_string_matching(/^\d+$/),
'RateLimit-Remaining' => a_string_matching(/^\d+$/),
'Retry-After' => a_string_matching(/^\d+$/)
diff --git a/spec/support/helpers/session_helpers.rb b/spec/support/helpers/session_helpers.rb
index 236585296e5..394a401afca 100644
--- a/spec/support/helpers/session_helpers.rb
+++ b/spec/support/helpers/session_helpers.rb
@@ -1,6 +1,22 @@
# frozen_string_literal: true
module SessionHelpers
+ # Stub a session in Redis, for use in request specs where we can't mock the session directly.
+ # This also needs the :clean_gitlab_redis_sessions tag on the spec.
+ def stub_session(session_hash)
+ unless RSpec.current_example.metadata[:clean_gitlab_redis_sessions]
+ raise 'Add :clean_gitlab_redis_sessions to your spec!'
+ end
+
+ session_id = Rack::Session::SessionId.new(SecureRandom.hex)
+
+ Gitlab::Redis::Sessions.with do |redis|
+ redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
+ end
+
+ cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ end
+
def expect_single_session_with_authenticated_ttl
expect_single_session_with_expiration(Settings.gitlab['session_expire_delay'] * 60)
end
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
index 8bffd1f71e9..a42a1fda62e 100644
--- a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
@@ -10,6 +10,8 @@ RSpec.shared_examples 'when the snippet is not found' do
end
RSpec.shared_examples 'snippet edit usage data counters' do
+ include SessionHelpers
+
context 'when user is sessionless' do
it 'does not track usage data actions' do
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action)
@@ -20,14 +22,7 @@ RSpec.shared_examples 'snippet edit usage data counters' do
context 'when user is not sessionless', :clean_gitlab_redis_sessions do
before do
- session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
- session_hash = { 'warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]] }
-
- Gitlab::Redis::Sessions.with do |redis|
- redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
- end
-
- cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ stub_session('warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]])
end
it 'tracks usage data actions', :clean_gitlab_redis_sessions do
diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
index b294467d482..c6c6c44dce8 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -580,3 +580,88 @@ RSpec.shared_examples 'rate-limited unauthenticated requests' do
end
end
end
+
+# Requires let variables:
+# * throttle_setting_prefix: "throttle_authenticated", "throttle_unauthenticated"
+RSpec.shared_examples 'rate-limited frontend API requests' do
+ let(:requests_per_period) { 1 }
+ let(:csrf_token) { SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH) }
+ let(:csrf_session) { { _csrf_token: csrf_token } }
+ let(:personal_access_token) { nil }
+
+ let(:api_path) { '/projects' }
+
+ # These don't actually exist, so a 404 is the expected response.
+ let(:files_api_path) { '/projects/1/repository/files/ref/path' }
+ let(:packages_api_path) { '/projects/1/packages/foo' }
+ let(:deprecated_api_path) { '/groups/1?with_projects=true' }
+
+ def get_api(path: api_path, csrf: false)
+ headers = csrf ? { 'X-CSRF-Token' => csrf_token } : nil
+ get api(path, personal_access_token: personal_access_token), headers: headers
+ end
+
+ def expect_not_found(&block)
+ yield
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ before do
+ stub_application_setting(
+ "#{throttle_setting_prefix}_enabled" => true,
+ "#{throttle_setting_prefix}_requests_per_period" => requests_per_period,
+ "#{throttle_setting_prefix}_api_enabled" => true,
+ "#{throttle_setting_prefix}_api_requests_per_period" => requests_per_period,
+ "#{throttle_setting_prefix}_web_enabled" => true,
+ "#{throttle_setting_prefix}_web_requests_per_period" => requests_per_period,
+ "#{throttle_setting_prefix}_files_api_enabled" => true,
+ "#{throttle_setting_prefix}_packages_api_enabled" => true,
+ "#{throttle_setting_prefix}_deprecated_api_enabled" => true
+ )
+
+ stub_session(csrf_session)
+ end
+
+ context 'with a CSRF token' do
+ it 'uses the rate limit for web requests' do
+ requests_per_period.times { get_api csrf: true }
+
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true }
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: files_api_path }
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: packages_api_path }
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: deprecated_api_path }
+
+ # API rate limit is not triggered yet
+ expect_ok { get_api }
+ expect_not_found { get_api path: files_api_path }
+ expect_not_found { get_api path: packages_api_path }
+ expect_not_found { get_api path: deprecated_api_path }
+ end
+
+ context 'without a CSRF session' do
+ let(:csrf_session) { nil }
+
+ it 'always uses the rate limit for API requests' do
+ requests_per_period.times { get_api csrf: true }
+
+ expect_rejection("#{throttle_setting_prefix}_api") { get_api csrf: true }
+ expect_rejection("#{throttle_setting_prefix}_api") { get_api }
+ end
+ end
+ end
+
+ context 'without a CSRF token' do
+ it 'uses the rate limit for API requests' do
+ requests_per_period.times { get_api }
+
+ expect_rejection("#{throttle_setting_prefix}_api") { get_api }
+
+ # Web and custom API rate limits are not triggered yet
+ expect_ok { get_api csrf: true }
+ expect_not_found { get_api path: files_api_path }
+ expect_not_found { get_api path: packages_api_path }
+ expect_not_found { get_api path: deprecated_api_path }
+ end
+ end
+end