summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-03-08 09:12:26 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-03-08 09:12:26 +0000
commit3c050fb24b757425987a7df4cb3497e1d792be8e (patch)
treee4031413c679ecf3e3126da1d1a8cbc3084d8377
parent14ac28d7c7fcf1aba321f521f3a980b03b7b090e (diff)
downloadgitlab-ce-3c050fb24b757425987a7df4cb3497e1d792be8e.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/package-and-test/main.gitlab-ci.yml18
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/work_items/components/widget_wrapper.vue2
-rw-r--r--app/views/projects/_self_monitoring_deprecation_notice.html.haml13
-rw-r--r--app/views/projects/empty.html.haml1
-rw-r--r--app/views/projects/show.html.haml2
-rw-r--r--db/post_migrate/20230307160251_rename_constraint_fk_rails_f601258b28_on_events_table.rb19
-rw-r--r--db/schema_migrations/202303071602511
-rw-r--r--doc/administration/instance_limits.md2
-rw-r--r--doc/architecture/blueprints/_template.md12
-rw-r--r--doc/architecture/blueprints/clickhouse_usage/index.md52
-rw-r--r--doc/tutorials/convert_personal_namespace_into_group.md95
-rw-r--r--doc/user/gitlab_com/index.md2
-rw-r--r--lib/gitlab/database/async_constraints/validators.rb20
-rw-r--r--lib/gitlab/database/async_constraints/validators/base.rb91
-rw-r--r--lib/gitlab/database/async_constraints/validators/check_constraint.rb19
-rw-r--r--lib/gitlab/database/async_constraints/validators/foreign_key.rb21
-rw-r--r--locale/gitlab.pot19
-rw-r--r--qa/qa/runtime/allure_report.rb6
-rw-r--r--spec/factories/gitlab/database/async_foreign_keys/postgres_async_constraint_validation.rb (renamed from spec/factories/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb)8
-rw-r--r--spec/lib/gitlab/database/async_constraints/validators/check_constraint_spec.rb20
-rw-r--r--spec/lib/gitlab/database/async_constraints/validators/foreign_key_spec.rb35
-rw-r--r--spec/lib/gitlab/database/async_constraints/validators_spec.rb21
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/async_constraints_validation_shared_examples.rb131
24 files changed, 600 insertions, 12 deletions
diff --git a/.gitlab/ci/package-and-test/main.gitlab-ci.yml b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
index 966c1c2f502..41b15e2edd3 100644
--- a/.gitlab/ci/package-and-test/main.gitlab-ci.yml
+++ b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
@@ -225,9 +225,11 @@ _ee:super-sidebar-nav:
QA_KNAPSACK_REPORT_NAME: ee-instance
QA_TESTS: ""
QA_SUPER_SIDEBAR_ENABLED: "true"
+ QA_ALLURE_RESULTS_DIRECTORY: tmp/allure-results-super-sidebar
GITLAB_QA_OPTS: --set-feature-flags super_sidebar_nav=enabled
allow_failure: true
rules:
+ - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
- !reference [.rules:test:manual, rules]
# ------------------------------------------
@@ -643,6 +645,22 @@ e2e-test-report:
ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
GIT_STRATEGY: none
+# Temporary separate test report for super-sidebar test job
+# TODO: remove once super-sidebar is on by default and enabled in tests
+# https://gitlab.com/groups/gitlab-org/-/epics/9044
+e2e-test-report-super-sidebar:
+ extends:
+ - .generate-allure-report-base
+ stage: report
+ needs:
+ - _ee:super-sidebar-nav
+ variables:
+ ALLURE_JOB_NAME: e2e-super-sidebar
+ ALLURE_RESULTS_GLOB: gitlab-qa-run-*/**/allure-results-super-sidebar
+ rules:
+ - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
+ - !reference [.rules:test:manual, rules]
+
upload-knapsack-report:
extends:
- .generate-knapsack-report-base
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 1157e876d49..3b6d9883a25 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-a2db715b5eda31493822fb8d923aceba7ecaf937
+659bff3b53d7b9a6894ac9404edbc45d02b49497
diff --git a/app/assets/javascripts/work_items/components/widget_wrapper.vue b/app/assets/javascripts/work_items/components/widget_wrapper.vue
index 44c757f8f59..d76a66f561c 100644
--- a/app/assets/javascripts/work_items/components/widget_wrapper.vue
+++ b/app/assets/javascripts/work_items/components/widget_wrapper.vue
@@ -44,7 +44,7 @@ export default {
<template>
<div class="gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-bg-gray-10 gl-mt-4">
<div
- class="gl-pl-5 gl-pr-4 gl-py-4 gl-display-flex gl-justify-content-space-between gl-bg-white"
+ class="gl-rounded-top-left-base gl-rounded-top-right-base gl-pl-5 gl-pr-4 gl-py-4 gl-display-flex gl-justify-content-space-between gl-bg-white"
:class="{ 'gl-border-b-1 gl-border-b-solid gl-border-b-gray-100': isOpen }"
>
<div class="gl-display-flex gl-flex-grow-1">
diff --git a/app/views/projects/_self_monitoring_deprecation_notice.html.haml b/app/views/projects/_self_monitoring_deprecation_notice.html.haml
new file mode 100644
index 00000000000..b9e32356688
--- /dev/null
+++ b/app/views/projects/_self_monitoring_deprecation_notice.html.haml
@@ -0,0 +1,13 @@
+- return unless project.self_monitoring?
+
+= content_for :page_level_alert do
+ .flash-container.flash-container-page.sticky
+ %div{ class: [container_class, 'limit-container-width', 'gl-pt-5!'] }
+ = render Pajamas::AlertComponent.new(title: _('Deprecation notice'),
+ variant: :danger,
+ alert_options: { class: 'gl-mb-3 gl-sticky' }) do |c|
+ = c.body do
+ - deprecation_link = '<a href="%{url}">'.html_safe % { url: help_page_path('update/deprecations', anchor: 'gitlab-self-monitoring-project') }
+ - removal_link = '<a href="%{url}">'.html_safe % { url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/348909' }
+ - opstrace_link = '<a href="%{url}">'.html_safe % { url: 'https://gitlab.com/groups/gitlab-org/-/epics/6976' }
+ = _("Self-monitoring was %{deprecation}deprecated%{link_end} in GitLab 14.9, and is %{removal}scheduled for removal%{link_end} in GitLab 16.0. For information on a possible replacement, %{opstrace}learn more about Opstrace%{link_end}.").html_safe % { deprecation: deprecation_link, removal: removal_link, opstrace: opstrace_link, link_end: '</a>'.html_safe }
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 43159a759f4..ca3f49bae95 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -8,6 +8,7 @@
= render "home_panel"
= render "archived_notice", project: @project
+= render "self_monitoring_deprecation_notice", project: @project
= render "invite_members_empty_project" if can_admin_project_member?(@project)
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 5fa70c3af32..f47f4ebc7ee 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -9,6 +9,8 @@
= render_if_exists 'shared/ultimate_feature_removal_banner', project: @project
= render partial: 'flash_messages', locals: { project: @project }
+= render "self_monitoring_deprecation_notice", project: @project
+
= render 'clusters_deprecation_alert'
= render "projects/last_push"
diff --git a/db/post_migrate/20230307160251_rename_constraint_fk_rails_f601258b28_on_events_table.rb b/db/post_migrate/20230307160251_rename_constraint_fk_rails_f601258b28_on_events_table.rb
new file mode 100644
index 00000000000..6b5ba4c3825
--- /dev/null
+++ b/db/post_migrate/20230307160251_rename_constraint_fk_rails_f601258b28_on_events_table.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class RenameConstraintFkRailsF601258b28OnEventsTable < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ TABLE_NAME = :events
+ FK_OLD_NAME = :fk_rails_f601258b28
+ FK_NEW_NAME = :fk_rails_0434b48643
+
+ def up
+ return unless foreign_key_exists?(TABLE_NAME, name: FK_OLD_NAME)
+
+ rename_constraint(TABLE_NAME, FK_OLD_NAME, FK_NEW_NAME)
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20230307160251 b/db/schema_migrations/20230307160251
new file mode 100644
index 00000000000..7265c801b46
--- /dev/null
+++ b/db/schema_migrations/20230307160251
@@ -0,0 +1 @@
+a7cf83ea7e94cc3d6a581cd89aab8274e86f5c195f1537395d72b275b96bd31c \ No newline at end of file
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index 1a5e5134e9b..7d10fdf770f 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -290,7 +290,7 @@ Plan.default.actual_limits.update!(group_hooks: 100)
Set the limit to `0` to disable it.
-The default maximum number of webhooks is `100` per project, `50` per group.
+The default maximum number of webhooks is `100` per project and `50` per group. Webhooks in a child group do not count towards the webhook limit of their parent group.
For GitLab.com, see the [webhook limits for GitLab.com](../user/gitlab_com/index.md#webhooks).
diff --git a/doc/architecture/blueprints/_template.md b/doc/architecture/blueprints/_template.md
index f7dea60e9b7..2756544e08a 100644
--- a/doc/architecture/blueprints/_template.md
+++ b/doc/architecture/blueprints/_template.md
@@ -125,6 +125,9 @@ but keep it simple! This should have enough detail that reviewers can
understand exactly what you're proposing, but should not include things like
API designs or implementation. The "Design Details" section below is for the
real nitty-gritty.
+
+You might want to consider including the pros and cons of the proposed solution so that they can be
+compared with the pros and cons of alternatives.
-->
## Design and implementation details
@@ -153,3 +156,12 @@ Diagrams authored in GitLab flavored markdown are preferred. In cases where
that is not feasible, images should be placed under `images/` in the same
directory as the `index.md` for the proposal.
-->
+
+## Alternative Solutions
+
+<!--
+It might be a good idea to include a list of alternative solutions or paths considered, although it is not required. Include pros and cons for
+each alternative solution/path.
+
+"Do nothing" and its pros and cons could be included in the list too.
+-->
diff --git a/doc/architecture/blueprints/clickhouse_usage/index.md b/doc/architecture/blueprints/clickhouse_usage/index.md
new file mode 100644
index 00000000000..390097a4cca
--- /dev/null
+++ b/doc/architecture/blueprints/clickhouse_usage/index.md
@@ -0,0 +1,52 @@
+---
+status: proposed
+creation-date: "2023-02-02"
+authors: [ "@nhxnguyen" ]
+coach: "@grzesiek"
+approvers: [ "@dorrino", "@nhxnguyen" ]
+owning-stage: "~devops::data_stores"
+participating-stages: ["~section::ops", "~section::dev"]
+---
+
+# ClickHouse Usage at GitLab
+
+## Summary
+
+[ClickHouse](https://clickhouse.com/) is an open-source column-oriented database management system. It can efficiently filter, aggregate, and sum across large numbers of rows. In FY23, GitLab selected ClickHouse as its standard data store for features with big data and insert-heavy requirements such as Observability and Analytics. This blueprint is a product of the [ClickHouse working group](https://about.gitlab.com/company/team/structure/working-groups/clickhouse-datastore/). It serves as a high-level blueprint to ClickHouse adoption at GitLab and references other blueprints addressing specific ClickHouse-related technical challenges.
+
+## Motivation
+
+In FY23-Q2, the Monitor:Observability team developed and shipped a [ClickHouse data platform](https://gitlab.com/groups/gitlab-org/-/epics/7772) to store and query data for Error Tracking and other observability features. Other teams have also begun to incorporate ClickHouse into their current or planned architectures. Given the growing interest in ClickHouse across product development teams, it is important to have a cohesive strategy for developing features using ClickHouse. This will allow teams to more efficiently leverage ClickHouse and ensure that we can maintain and support this functionality effectively for SaaS and self-managed customers.
+
+### Goals
+
+As ClickHouse has already been selected for use at GitLab, our main goal now is to ensure successful adoption of ClickHouse across GitLab. It is helpful to break down this goal according to the different phases of the product development workflow.
+
+1. Plan: Make it easy for development teams to understand if ClickHouse is the right fit for their feature.
+1. Develop and Test: Give teams the best practices and frameworks to develop ClickHouse-backed features.
+1. Launch: Support ClickHouse-backed features for SaaS and self-managed.
+1. Improve: Successfully scale our usage of ClickHouse.
+
+### Non-Goals
+
+## Proposals
+
+The following are links to proposals in the form of blueprints that address technical challenges to using ClickHouse across a wide variety of features.
+
+1. Scalable data ingestion pipeline.
+ - How do we ingest large volumes of data from GitLab into ClickHouse either directly or by replicating existing data?
+1. Supporting ClickHouse for self-managed installations.
+ - For which use-cases and scales does it make sense to run ClickHouse for self-managed and what are the associated costs?
+ - How can we best support self-managed installation of ClickHouse for different types/sizes of environments?
+ - Consider using the [Opstrace ClickHouse operator](https://gitlab.com/gitlab-org/opstrace/opstrace/-/tree/main/clickhouse-operator) as the basis for a canonical distribution.
+ - Consider exposing Clickhouse backend as [GitLab Plus](https://gitlab.com/groups/gitlab-org/-/epics/308) to combine benefits of using self-managed instance and GitLab-managed database.
+ - Should we develop abstractions for querying and data ingestion to avoid requiring ClickHouse for small-scale installations?
+1. Abstraction layer for features to leverage both ClickHouse or PostreSQL.
+ - What are the benefits and tradeoffs? For example, how would this impact our automated migration and query testing?
+1. Security recommendations and secure defaults for ClickHouse usage.
+
+Note that we are still formulating proposals and will update the blueprint accordingly.
+
+## Best Practices
+
+Best practices and guidelines for developing performant and scalable features using ClickHouse are located in the [ClickHouse developer documentation](../../../development/database/clickhouse/index.md).
diff --git a/doc/tutorials/convert_personal_namespace_into_group.md b/doc/tutorials/convert_personal_namespace_into_group.md
new file mode 100644
index 00000000000..e272d854a35
--- /dev/null
+++ b/doc/tutorials/convert_personal_namespace_into_group.md
@@ -0,0 +1,95 @@
+---
+stage: none
+group: Tutorials
+info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+---
+
+# Tutorial: Convert a personal namespace into a group **(FREE SAAS)**
+
+If you've started out on GitLab with a personal [namespace](../user/namespace/index.md), but now find
+that you've outgrown its capabilities and its limitations hinder the collaboration on your projects,
+you might want to switch to a group namespace instead.
+A group namespace allows you to create multiple subgroups, and manage their members and permissions.
+
+You don't have to start from scratch - you can create a new group
+and move your existing projects to the group to get the added benefits.
+To find out how, see [Tutorial: Move your personal project to a group](move_personal_project_to_a_group.md).
+
+But you can go one step further and convert your personal namespace into a group namespace,
+so you get to keep the existing username and URL. For example, if your username is `alex`,
+you can continue using the `https://gitlab.example.com/alex` URL for your group.
+
+This tutorial shows you how to convert your personal namespace into a group namespace
+using the following steps:
+
+1. [Create a group](#create-a-group).
+1. [Transfer projects from the personal namespace to the group](#transfer-projects-from-the-personal-namespace-to-the-group).
+1. [Rename the original username](#rename-the-original-username).
+1. [Rename the new group namespace to the original username](#rename-the-new-group-namespace-to-the-original-username).
+
+For example, if your username for a personal namespace is `alex`, first create a group namespace named `alex-group`.
+Then, move all projects from the `alex` to the `alex-group` namespace. Finally,
+rename the `alex` namespace to `alex-user`, and `alex-group` namespace to the now available `alex` username.
+
+## Create a group
+
+1. On the top bar, select **Main menu > Groups > View all groups**.
+1. On the right of the page, select **New group**.
+1. In **Group name**, enter a name for the group.
+1. In **Group URL**, enter a path for the group, which is used as the namespace.
+ Don't worry about the actual path, this is only temporary. You'll change this URL to the username of the personal namespace in the [final step](#rename-the-new-group-namespace-to-the-original-username).
+1. Choose the [visibility level](../user/public_access.md).
+1. Optional. Fill in information to personalize your experience.
+1. Select **Create group**.
+
+## Transfer projects from the personal namespace to the group
+
+Next, you must transfer your projects from the personal namespace to the new group.
+You can transfer only one project at a time, so if you want to transfer multiple projects,
+you must perform the steps below for each project.
+
+Before you start the transfer process, make sure you:
+
+- Have the Owner role for the project.
+- Remove [container images](../user/packages/container_registry/index.md#move-or-rename-container-registry-repositories).
+ You can't transfer a project that contains container images.
+- Remove npm packages. You can't update the root namespace of a project that contains npm packages.
+
+To transfer a project to a group:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Advanced**.
+1. Under **Transfer project**, choose the group to transfer the project to.
+1. Select **Transfer project**.
+1. Enter the project's name and select **Confirm**.
+
+## Rename the original username
+
+Next, rename the original username of the personal namespace, so that the username becomes available for the new group namespace.
+You can keep on using the personal namespace for other personal projects, or [delete that user account](../user/profile/account/delete_account.md)
+
+From the moment you rename the personal namespace, the username becomes available, so it's possible that someone else registers an account with it. To avoid this, you should [rename the new group](#rename-the-new-group-namespace-to-the-original-username) as soon as possible.
+
+To [change a user's username](../user/profile/index.md#change-your-username):
+
+1. On the top bar, in the top-right corner, select your avatar.
+1. Select **Edit profile**.
+1. On the left sidebar, select **Account**.
+1. In the **Change username** section, enter a new username as the path.
+1. Select **Update username**.
+
+## Rename the new group namespace to the original username
+
+Finally, rename the new group's URL to the username of the original personal namespace.
+
+To [change your group path](../user/group/manage.md#change-a-groups-path) (group URL):
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General page**.
+1. Expand the **Advanced** section.
+1. Under **Change group URL**, enter the user's original username.
+1. Select **Change group URL**.
+
+That's it! You have now converted a personal namespace into a group, which opens up new possibilities of
+working on projects and collaborating with more members.
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index b80341c2e68..907d3eaa532 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -248,7 +248,7 @@ The limit varies depending on your plan and the number of seats in your subscrip
| Setting | Default for GitLab.com |
|----------------------|-------------------------|
-| Number of webhooks | `100` per project, `50` per group |
+| Number of webhooks | `100` per project, `50` per group (subgroup webhooks are not counted towards parent group limits ) |
| Maximum payload size | 25 MB |
| Timeout | 10 seconds |
diff --git a/lib/gitlab/database/async_constraints/validators.rb b/lib/gitlab/database/async_constraints/validators.rb
new file mode 100644
index 00000000000..39792a5ee8e
--- /dev/null
+++ b/lib/gitlab/database/async_constraints/validators.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module AsyncConstraints
+ module Validators
+ MAPPING = {
+ foreign_key: Validators::ForeignKey,
+ check_constraint: Validators::CheckConstraint
+ }.freeze
+
+ def self.for(record)
+ MAPPING
+ .fetch(record.constraint_type.to_sym)
+ .new(record)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/async_constraints/validators/base.rb b/lib/gitlab/database/async_constraints/validators/base.rb
new file mode 100644
index 00000000000..39a72955d63
--- /dev/null
+++ b/lib/gitlab/database/async_constraints/validators/base.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module AsyncConstraints
+ module Validators
+ class Base
+ include AsyncDdlExclusiveLeaseGuard
+ extend ::Gitlab::Utils::Override
+
+ TIMEOUT_PER_ACTION = 1.day
+ STATEMENT_TIMEOUT = 12.hours
+
+ def initialize(record)
+ @record = record
+ end
+
+ def perform
+ try_obtain_lease do
+ if constraint_exists?
+ log_info('Starting to validate constraint')
+ validate_constraint_with_error_handling
+ log_info('Finished validating constraint')
+ else
+ log_info(skip_log_message)
+ record.destroy!
+ end
+ end
+ end
+
+ private
+
+ attr_reader :record
+
+ delegate :connection, :name, :table_name, :connection_db_config, to: :record
+
+ def constraint_exists?; end
+
+ def validate_constraint_with_error_handling
+ validate_constraint
+ record.destroy!
+ rescue StandardError => error
+ record.handle_exception!(error)
+
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
+ Gitlab::AppLogger.error(message: error.message, **logging_options)
+ end
+
+ def validate_constraint
+ set_statement_timeout do
+ connection.execute(<<~SQL.squish)
+ ALTER TABLE #{connection.quote_table_name(table_name)}
+ VALIDATE CONSTRAINT #{connection.quote_column_name(name)};
+ SQL
+ end
+ end
+
+ def set_statement_timeout
+ connection.execute(format("SET statement_timeout TO '%ds'", STATEMENT_TIMEOUT))
+ yield
+ ensure
+ connection.execute('RESET statement_timeout')
+ end
+
+ def lease_timeout
+ TIMEOUT_PER_ACTION
+ end
+
+ def log_info(message)
+ Gitlab::AppLogger.info(message: message, **logging_options)
+ end
+
+ def skip_log_message
+ "Skipping #{name} validation since it does not exist. " \
+ "The queuing entry will be deleted"
+ end
+
+ def logging_options
+ {
+ class: self.class.name.to_s,
+ connection_name: database_config_name,
+ constraint_name: name,
+ constraint_type: record.constraint_type,
+ table_name: table_name
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/async_constraints/validators/check_constraint.rb b/lib/gitlab/database/async_constraints/validators/check_constraint.rb
new file mode 100644
index 00000000000..695ecdebc9f
--- /dev/null
+++ b/lib/gitlab/database/async_constraints/validators/check_constraint.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module AsyncConstraints
+ module Validators
+ class CheckConstraint < Base
+ private
+
+ override :constraint_exists?
+ def constraint_exists?
+ Gitlab::Database::Migrations::ConstraintsHelpers
+ .check_constraint_exists?(table_name, name, connection: connection)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/async_constraints/validators/foreign_key.rb b/lib/gitlab/database/async_constraints/validators/foreign_key.rb
new file mode 100644
index 00000000000..ff6b807c982
--- /dev/null
+++ b/lib/gitlab/database/async_constraints/validators/foreign_key.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module AsyncConstraints
+ module Validators
+ class ForeignKey < Base
+ private
+
+ override :constraint_exists?
+ def constraint_exists?
+ Gitlab::Database::PostgresForeignKey
+ .by_constrained_table_name_or_identifier(table_name)
+ .by_name(name)
+ .exists?
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index db3e396c5e2..baa0fc03117 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6485,9 +6485,6 @@ msgstr ""
msgid "BillingPlans|Includes free static websites"
msgstr ""
-msgid "BillingPlans|Learn more"
-msgstr ""
-
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Ultimate."
msgstr ""
@@ -14349,6 +14346,9 @@ msgstr ""
msgid "Deprecated API rate limits"
msgstr ""
+msgid "Deprecation notice"
+msgstr ""
+
msgid "Deprecations|For information on a possible replacement %{epicStart} learn more about Opstrace %{epicEnd}."
msgstr ""
@@ -37948,10 +37948,10 @@ msgstr ""
msgid "ScanExecutionPolicy|%{rules} actions for the %{scopes} %{branches} %{agents} %{namespaces}"
msgstr ""
-msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} with tags %{tags}"
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan on runner that %{tags}"
msgstr ""
-msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with tags %{tags}"
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile} on runner that %{tags}"
msgstr ""
msgid "ScanExecutionPolicy|A pipeline is run"
@@ -38002,9 +38002,15 @@ msgstr ""
msgid "ScanExecutionPolicy|branch"
msgstr ""
+msgid "ScanExecutionPolicy|has specific tag"
+msgstr ""
+
msgid "ScanExecutionPolicy|in namespaces"
msgstr ""
+msgid "ScanExecutionPolicy|selected automatically"
+msgstr ""
+
msgid "ScanResultPolicy|%{count} licenses"
msgstr ""
@@ -39689,6 +39695,9 @@ msgstr ""
msgid "Self-monitoring project was not deleted. Please check logs for any error messages"
msgstr ""
+msgid "Self-monitoring was %{deprecation}deprecated%{link_end} in GitLab 14.9, and is %{removal}scheduled for removal%{link_end} in GitLab 16.0. For information on a possible replacement, %{opstrace}learn more about Opstrace%{link_end}."
+msgstr ""
+
msgid "SelfMonitoring|Activate or deactivate instance self-monitoring."
msgstr ""
diff --git a/qa/qa/runtime/allure_report.rb b/qa/qa/runtime/allure_report.rb
index a9152a5555c..f92bdf74695 100644
--- a/qa/qa/runtime/allure_report.rb
+++ b/qa/qa/runtime/allure_report.rb
@@ -29,7 +29,7 @@ module QA
env_matcher = /^(?<env>\w{2}:\S+)/
AllureRspec.configure do |config|
- config.results_directory = 'tmp/allure-results'
+ config.results_directory = ENV['QA_ALLURE_RESULTS_DIRECTORY'] || 'tmp/allure-results'
config.clean_results_directory = true
# automatically attach links to testcases and issues
@@ -77,7 +77,7 @@ module QA
config.add_formatter(QA::Support::Formatters::AllureMetadataFormatter)
config.add_formatter(AllureRspecFormatter)
- config.append_after do |example|
+ config.append_after do
Allure.add_attachment(
name: 'browser.log',
source: Capybara.current_session.driver.browser.logs.get(:browser).map(&:to_s).join("\n\n"),
@@ -92,7 +92,7 @@ module QA
#
# @return [Hash]
def environment_info
- lambda do
+ -> do
return {} unless Env.admin_personal_access_token || Env.personal_access_token
client = Env.admin_personal_access_token ? API::Client.as_admin : API::Client.new
diff --git a/spec/factories/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb b/spec/factories/gitlab/database/async_foreign_keys/postgres_async_constraint_validation.rb
index 23f4049f1cb..81f67e958c0 100644
--- a/spec/factories/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb
+++ b/spec/factories/gitlab/database/async_foreign_keys/postgres_async_constraint_validation.rb
@@ -5,5 +5,13 @@ FactoryBot.define do
class: 'Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation' do
sequence(:name) { |n| "fk_users_id_#{n}" }
table_name { "users" }
+
+ trait :foreign_key do
+ constraint_type { :foreign_key }
+ end
+
+ trait :check_constraint do
+ constraint_type { :check_constraint }
+ end
end
end
diff --git a/spec/lib/gitlab/database/async_constraints/validators/check_constraint_spec.rb b/spec/lib/gitlab/database/async_constraints/validators/check_constraint_spec.rb
new file mode 100644
index 00000000000..7622b39feb1
--- /dev/null
+++ b/spec/lib/gitlab/database/async_constraints/validators/check_constraint_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::AsyncConstraints::Validators::CheckConstraint, feature_category: :database do
+ it_behaves_like 'async constraints validation' do
+ let(:constraint_type) { :check_constraint }
+
+ before do
+ connection.create_table(table_name) do |t|
+ t.integer :parent_id
+ end
+
+ connection.execute(<<~SQL.squish)
+ ALTER TABLE #{table_name} ADD CONSTRAINT #{constraint_name}
+ CHECK ( parent_id = 101 ) NOT VALID;
+ SQL
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/async_constraints/validators/foreign_key_spec.rb b/spec/lib/gitlab/database/async_constraints/validators/foreign_key_spec.rb
new file mode 100644
index 00000000000..0e345e0e9ae
--- /dev/null
+++ b/spec/lib/gitlab/database/async_constraints/validators/foreign_key_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::AsyncConstraints::Validators::ForeignKey, feature_category: :database do
+ it_behaves_like 'async constraints validation' do
+ let(:constraint_type) { :foreign_key }
+
+ before do
+ connection.create_table(table_name) do |t|
+ t.references :parent, foreign_key: { to_table: table_name, validate: false, name: constraint_name }
+ end
+ end
+
+ context 'with fully qualified table names' do
+ let(:validation) do
+ create(:postgres_async_constraint_validation,
+ table_name: "public.#{table_name}",
+ name: constraint_name,
+ constraint_type: constraint_type
+ )
+ end
+
+ it 'validates the constraint' do
+ allow(connection).to receive(:execute).and_call_original
+
+ expect(connection).to receive(:execute)
+ .with(/ALTER TABLE "public"."#{table_name}" VALIDATE CONSTRAINT "#{constraint_name}";/)
+ .ordered.and_call_original
+
+ subject.perform
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/async_constraints/validators_spec.rb b/spec/lib/gitlab/database/async_constraints/validators_spec.rb
new file mode 100644
index 00000000000..e903b79dd1b
--- /dev/null
+++ b/spec/lib/gitlab/database/async_constraints/validators_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::AsyncConstraints::Validators, feature_category: :database do
+ describe '.for' do
+ subject { described_class.for(record) }
+
+ context 'with foreign keys validations' do
+ let(:record) { build(:postgres_async_constraint_validation, :foreign_key) }
+
+ it { is_expected.to be_a(described_class::ForeignKey) }
+ end
+
+ context 'with check constraint validations' do
+ let(:record) { build(:postgres_async_constraint_validation, :check_constraint) }
+
+ it { is_expected.to be_a(described_class::CheckConstraint) }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/database/async_constraints_validation_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/async_constraints_validation_shared_examples.rb
new file mode 100644
index 00000000000..b9d71183851
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/database/async_constraints_validation_shared_examples.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'async constraints validation' do
+ include ExclusiveLeaseHelpers
+
+ let!(:lease) { stub_exclusive_lease(lease_key, :uuid, timeout: lease_timeout) }
+ let(:lease_key) { "gitlab/database/asyncddl/actions/#{Gitlab::Database::PRIMARY_DATABASE_NAME}" }
+ let(:lease_timeout) { described_class::TIMEOUT_PER_ACTION }
+
+ let(:constraints_model) { Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation }
+ let(:table_name) { '_test_async_constraints' }
+ let(:constraint_name) { 'constraint_parent_id' }
+
+ let(:validation) do
+ create(:postgres_async_constraint_validation,
+ table_name: table_name,
+ name: constraint_name,
+ constraint_type: constraint_type)
+ end
+
+ let(:connection) { validation.connection }
+
+ subject { described_class.new(validation) }
+
+ it 'validates the constraint while controlling statement timeout' do
+ allow(connection).to receive(:execute).and_call_original
+ expect(connection).to receive(:execute)
+ .with("SET statement_timeout TO '43200s'").ordered.and_call_original
+ expect(connection).to receive(:execute)
+ .with(/ALTER TABLE "#{table_name}" VALIDATE CONSTRAINT "#{constraint_name}";/).ordered.and_call_original
+ expect(connection).to receive(:execute)
+ .with("RESET statement_timeout").ordered.and_call_original
+
+ subject.perform
+ end
+
+ it 'removes the constraint validation record from table' do
+ expect(validation).to receive(:destroy!).and_call_original
+
+ expect { subject.perform }.to change { constraints_model.count }.by(-1)
+ end
+
+ it 'skips logic if not able to acquire exclusive lease' do
+ expect(lease).to receive(:try_obtain).ordered.and_return(false)
+ expect(connection).not_to receive(:execute).with(/ALTER TABLE/)
+ expect(validation).not_to receive(:destroy!)
+
+ expect { subject.perform }.not_to change { constraints_model.count }
+ end
+
+ it 'logs messages around execution' do
+ allow(Gitlab::AppLogger).to receive(:info).and_call_original
+
+ subject.perform
+
+ expect(Gitlab::AppLogger)
+ .to have_received(:info)
+ .with(a_hash_including(message: 'Starting to validate constraint'))
+
+ expect(Gitlab::AppLogger)
+ .to have_received(:info)
+ .with(a_hash_including(message: 'Finished validating constraint'))
+ end
+
+ context 'when the constraint does not exist' do
+ before do
+ connection.create_table(table_name, force: true)
+ end
+
+ it 'skips validation and removes the record' do
+ expect(connection).not_to receive(:execute).with(/ALTER TABLE/)
+
+ expect { subject.perform }.to change { constraints_model.count }.by(-1)
+ end
+
+ it 'logs an appropriate message' do
+ expected_message = /Skipping #{constraint_name} validation since it does not exist/
+
+ allow(Gitlab::AppLogger).to receive(:info).and_call_original
+
+ subject.perform
+
+ expect(Gitlab::AppLogger)
+ .to have_received(:info)
+ .with(a_hash_including(message: expected_message))
+ end
+ end
+
+ context 'with error handling' do
+ before do
+ allow(connection).to receive(:execute).and_call_original
+
+ allow(connection).to receive(:execute)
+ .with(/ALTER TABLE "#{table_name}" VALIDATE CONSTRAINT "#{constraint_name}";/)
+ .and_raise(ActiveRecord::StatementInvalid)
+ end
+
+ context 'on production' do
+ before do
+ allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
+ end
+
+ it 'increases execution attempts' do
+ expect { subject.perform }.to change { validation.attempts }.by(1)
+
+ expect(validation.last_error).to be_present
+ expect(validation).not_to be_destroyed
+ end
+
+ it 'logs an error message including the constraint_name' do
+ expect(Gitlab::AppLogger)
+ .to receive(:error)
+ .with(a_hash_including(:message, :constraint_name))
+ .and_call_original
+
+ subject.perform
+ end
+ end
+
+ context 'on development' do
+ it 'also raises errors' do
+ expect { subject.perform }
+ .to raise_error(ActiveRecord::StatementInvalid)
+ .and change { validation.attempts }.by(1)
+
+ expect(validation.last_error).to be_present
+ expect(validation).not_to be_destroyed
+ end
+ end
+ end
+end