Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									ba557e8fea
								
							
						
					
					
						commit
						7503415f61
					
				|  | @ -3160,7 +3160,6 @@ Layout/LineLength: | |||
|     - 'lib/gitlab/database/reindexing/coordinator.rb' | ||||
|     - 'lib/gitlab/database/reindexing/grafana_notifier.rb' | ||||
|     - 'lib/gitlab/database/reindexing/reindex_concurrently.rb' | ||||
|     - 'lib/gitlab/database/schema_cleaner.rb' | ||||
|     - 'lib/gitlab/database/schema_migrations/context.rb' | ||||
|     - 'lib/gitlab/database/similarity_score.rb' | ||||
|     - 'lib/gitlab/database/with_lock_retries.rb' | ||||
|  |  | |||
|  | @ -5,9 +5,9 @@ import { getCookie, setCookie } from '~/lib/utils/common_utils'; | |||
| import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue'; | ||||
| import { VSA_METRICS_GROUPS } from '~/analytics/shared/constants'; | ||||
| import { toYmd } from '~/analytics/shared/utils'; | ||||
| import PathNavigation from '~/cycle_analytics/components/path_navigation.vue'; | ||||
| import StageTable from '~/cycle_analytics/components/stage_table.vue'; | ||||
| import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue'; | ||||
| import PathNavigation from '~/analytics/cycle_analytics/components/path_navigation.vue'; | ||||
| import StageTable from '~/analytics/cycle_analytics/components/stage_table.vue'; | ||||
| import ValueStreamFilters from '~/analytics/cycle_analytics/components/value_stream_filters.vue'; | ||||
| import UrlSync from '~/vue_shared/components/url_sync.vue'; | ||||
| import { __ } from '~/locale'; | ||||
| import { SUMMARY_METRICS_REQUEST, METRICS_REQUESTS } from '../constants'; | ||||
|  | @ -8,7 +8,7 @@ import { | |||
|   GlTable, | ||||
|   GlBadge, | ||||
| } from '@gitlab/ui'; | ||||
| import FormattedStageCount from '~/cycle_analytics/components/formatted_stage_count.vue'; | ||||
| import FormattedStageCount from '~/analytics/cycle_analytics/components/formatted_stage_count.vue'; | ||||
| import { __ } from '~/locale'; | ||||
| import Tracking from '~/tracking'; | ||||
| import { | ||||
|  | @ -3,7 +3,7 @@ import { | |||
|   extractFilterQueryParameters, | ||||
|   extractPaginationQueryParameters, | ||||
| } from '~/analytics/shared/utils'; | ||||
| import Translate from '../vue_shared/translate'; | ||||
| import Translate from '~/vue_shared/translate'; | ||||
| import CycleAnalytics from './components/base.vue'; | ||||
| import createStore from './store'; | ||||
| import { buildCycleAnalyticsInitialData } from './utils'; | ||||
|  | @ -1,7 +1,7 @@ | |||
| import { | ||||
|   PAGINATION_SORT_FIELD_END_EVENT, | ||||
|   PAGINATION_SORT_DIRECTION_DESC, | ||||
| } from '~/cycle_analytics/constants'; | ||||
| } from '~/analytics/cycle_analytics/constants'; | ||||
| 
 | ||||
| export default () => ({ | ||||
|   id: null, | ||||
|  | @ -1,3 +1,3 @@ | |||
| import initCycleAnalytics from '~/cycle_analytics'; | ||||
| import initCycleAnalytics from '~/analytics/cycle_analytics'; | ||||
| 
 | ||||
| initCycleAnalytics(); | ||||
|  |  | |||
|  | @ -2334,9 +2334,7 @@ class User < ApplicationRecord | |||
|   end | ||||
| 
 | ||||
|   def check_password_weakness | ||||
|     if Feature.enabled?(:block_weak_passwords) && | ||||
|         password.present? && | ||||
|         Security::WeakPasswords.weak_for_user?(password, self) | ||||
|     if password.present? && Security::WeakPasswords.weak_for_user?(password, self) | ||||
|       errors.add(:password, _('must not contain commonly used combinations of words and letters')) | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: block_weak_passwords | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86310 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363445 | ||||
| milestone: '15.4' | ||||
| type: development | ||||
| group: group::authentication and authorization | ||||
| default_enabled: false | ||||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: dast_api_scanner | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73564 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345837 | ||||
| milestone: '14.7' | ||||
| type: development | ||||
| group: group::dynamic analysis | ||||
| default_enabled: true | ||||
|  | @ -0,0 +1,8 @@ | |||
| --- | ||||
| name: automatic_lock_writes_on_table | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99287 | ||||
| rollout_issue_url: | ||||
| milestone: '15.7' | ||||
| type: ops | ||||
| group: group::pods | ||||
| default_enabled: false | ||||
|  | @ -14,7 +14,7 @@ A short downtime is expected for all methods. | |||
| 
 | ||||
| ## Omnibus installations | ||||
| 
 | ||||
| If you have used the [Omnibus packages](https://about.gitlab.com/install/) to install GitLab, then | ||||
| If you have used the [Omnibus packages](https://about.gitlab.com/install/) to install GitLab, | ||||
| you should already have `gitlab-ctl` in your `PATH`. | ||||
| 
 | ||||
| `gitlab-ctl` interacts with the Omnibus packages and can be used to restart the | ||||
|  | @ -88,16 +88,14 @@ sudo gitlab-ctl reconfigure | |||
| Reconfiguring GitLab should occur in the event that something in its | ||||
| configuration (`/etc/gitlab/gitlab.rb`) has changed. | ||||
| 
 | ||||
| When you run this command, [Chef](https://www.chef.io/products/chef-infra), the underlying configuration management | ||||
| application that powers Omnibus GitLab, makes sure that all things like directories, | ||||
| permissions, and services are in place and in the same shape that they were | ||||
| initially shipped. | ||||
| When you run `gitlab-ctl reconfigure`, [Chef](https://www.chef.io/products/chef-infra), | ||||
| the underlying configuration management application that powers Omnibus GitLab, runs some checks. | ||||
| Chef ensures directories, permissions, and services are in place and working. | ||||
| 
 | ||||
| It also [restarts GitLab components](#how-to-restart-gitlab) | ||||
| where needed, if any of their configuration files have changed. | ||||
| Chef also [restarts GitLab components](#how-to-restart-gitlab) if any of their configuration files have changed. | ||||
| 
 | ||||
| If you manually edit any files in `/var/opt/gitlab` that are managed by Chef, | ||||
| running reconfigure reverts the changes and restarts the services that | ||||
| running `reconfigure` reverts the changes and restarts the services that | ||||
| depend on those files. | ||||
| 
 | ||||
| ## Installations from source | ||||
|  | @ -118,7 +116,7 @@ This should restart Puma, Sidekiq, GitLab Workhorse, and [Mailroom](reply_by_ema | |||
| 
 | ||||
| ## Helm chart installations | ||||
| 
 | ||||
| There is no single command to restart the entire GitLab application installed via | ||||
| There is no single command to restart the entire GitLab application installed through | ||||
| the [cloud-native Helm chart](https://docs.gitlab.com/charts/). Usually, it should be | ||||
| enough to restart a specific component separately (for example, `gitaly`, `puma`, | ||||
| `workhorse`, or `gitlab-shell`) by deleting all the pods related to it: | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w | |||
| 
 | ||||
| # Uploads administration **(FREE SELF)** | ||||
| 
 | ||||
| Uploads represent all user data that may be sent to GitLab as a single file. As an example, avatars and notes' attachments are uploads. Uploads are integral to GitLab functionality, and therefore cannot be disabled. | ||||
| Uploads represent all user data that may be sent to GitLab as a single file. For example, avatars and note attachments are uploads. Uploads are integral to GitLab functionality and therefore cannot be disabled. | ||||
| 
 | ||||
| ## Using local storage | ||||
| 
 | ||||
|  | @ -14,15 +14,15 @@ This is the default configuration. To change the location where the uploads are | |||
| stored locally, use the steps in this section based on your installation method: | ||||
| 
 | ||||
| NOTE: | ||||
| For historical reasons, instance level uploads (for example the [favicon](../user/admin_area/appearance.md#favicon)) are stored into a base directory, | ||||
| which by default is `uploads/-/system`. It is strongly discouraged to change the base | ||||
| directory on an existing GitLab installation. | ||||
| For historical reasons, uploads for the whole instance (for example the [favicon](../user/admin_area/appearance.md#favicon)) are stored in a base directory, | ||||
| which by default is `uploads/-/system`. Changing the base | ||||
| directory on an existing GitLab installation is strongly discouraged. | ||||
| 
 | ||||
| **In Omnibus GitLab installations:** | ||||
| 
 | ||||
| _The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads`._ | ||||
| 
 | ||||
| 1. To change the storage path for example to `/mnt/storage/uploads`, edit | ||||
| 1. To change the storage path, for example to `/mnt/storage/uploads`, edit | ||||
|    `/etc/gitlab/gitlab.rb` and add the following line: | ||||
| 
 | ||||
|    ```ruby | ||||
|  | @ -38,7 +38,7 @@ _The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads`._ | |||
| _The uploads are stored by default in | ||||
| `/home/git/gitlab/public/uploads`._ | ||||
| 
 | ||||
| 1. To change the storage path for example to `/mnt/storage/uploads`, edit | ||||
| 1. To change the storage path, for example to `/mnt/storage/uploads`, edit | ||||
|    `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: | ||||
| 
 | ||||
|    ```yaml | ||||
|  | @ -57,7 +57,7 @@ This configuration relies on valid AWS credentials to be configured already. | |||
| 
 | ||||
| [Read more about using object storage with GitLab](object_storage.md). | ||||
| 
 | ||||
| We recommend using the [consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration). The following instructions apply to the original configuration format. | ||||
| You should use the [consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration). The following instructions apply to the original configuration format. | ||||
| 
 | ||||
| ### Object Storage Settings | ||||
| 
 | ||||
|  |  | |||
|  | @ -2098,8 +2098,8 @@ Input type: `DastSiteProfileCreateInput` | |||
| | <a id="mutationdastsiteprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site profile belongs to. | | ||||
| | <a id="mutationdastsiteprofilecreateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. | | ||||
| | <a id="mutationdastsiteprofilecreaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. | | ||||
| | <a id="mutationdastsiteprofilecreatescanfilepath"></a>`scanFilePath` | [`String`](#string) | File Path or URL used as input for the scan method. Will not be saved or updated if `dast_api_scanner` feature flag is disabled. | | ||||
| | <a id="mutationdastsiteprofilecreatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. Is not saved or updated if `dast_api_scanner` feature flag is disabled. | | ||||
| | <a id="mutationdastsiteprofilecreatescanfilepath"></a>`scanFilePath` | [`String`](#string) | File Path or URL used as input for the scan method. | | ||||
| | <a id="mutationdastsiteprofilecreatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. | | ||||
| | <a id="mutationdastsiteprofilecreatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. | | ||||
| | <a id="mutationdastsiteprofilecreatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. | | ||||
| 
 | ||||
|  | @ -2146,8 +2146,8 @@ Input type: `DastSiteProfileUpdateInput` | |||
| | <a id="mutationdastsiteprofileupdateid"></a>`id` | [`DastSiteProfileID!`](#dastsiteprofileid) | ID of the site profile to be updated. | | ||||
| | <a id="mutationdastsiteprofileupdateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. | | ||||
| | <a id="mutationdastsiteprofileupdaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. | | ||||
| | <a id="mutationdastsiteprofileupdatescanfilepath"></a>`scanFilePath` | [`String`](#string) | File Path or URL used as input for the scan method. Will not be saved or updated if `dast_api_scanner` feature flag is disabled. | | ||||
| | <a id="mutationdastsiteprofileupdatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. Is not saved or updated if `dast_api_scanner` feature flag is disabled. | | ||||
| | <a id="mutationdastsiteprofileupdatescanfilepath"></a>`scanFilePath` | [`String`](#string) | File Path or URL used as input for the scan method. | | ||||
| | <a id="mutationdastsiteprofileupdatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. | | ||||
| | <a id="mutationdastsiteprofileupdatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. | | ||||
| | <a id="mutationdastsiteprofileupdatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. | | ||||
| 
 | ||||
|  | @ -11708,8 +11708,8 @@ Represents a DAST Site Profile. | |||
| | <a id="dastsiteprofileprofilename"></a>`profileName` | [`String`](#string) | Name of the site profile. | | ||||
| | <a id="dastsiteprofilereferencedinsecuritypolicies"></a>`referencedInSecurityPolicies` | [`[String!]`](#string) | List of security policy names that are referencing given project. | | ||||
| | <a id="dastsiteprofilerequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. | | ||||
| | <a id="dastsiteprofilescanfilepath"></a>`scanFilePath` | [`String`](#string) | Scan File Path used as input for the scanner. Will always return `null` if `dast_api_scanner` feature flag is disabled. | | ||||
| | <a id="dastsiteprofilescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method used by the scanner. Always returns `null` if `dast_api_scanner` feature flag is disabled. | | ||||
| | <a id="dastsiteprofilescanfilepath"></a>`scanFilePath` | [`String`](#string) | Scan File Path used as input for the scanner. | | ||||
| | <a id="dastsiteprofilescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method used by the scanner. | | ||||
| | <a id="dastsiteprofiletargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. | | ||||
| | <a id="dastsiteprofiletargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. | | ||||
| | <a id="dastsiteprofileuserpermissions"></a>`userPermissions` | [`DastSiteProfilePermissions!`](#dastsiteprofilepermissions) | Permissions for the current user on the resource. | | ||||
|  |  | |||
|  | @ -61,12 +61,8 @@ Self-managed installations can configure the following additional password requi | |||
| ## Block weak passwords | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23610) in GitLab 15.4 [with a flag](../../administration/feature_flags.md) named `block_weak_passwords`, weak passwords aren't accepted. Disabled by default on self-managed. | ||||
| > - [Enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/363445) on GitLab.com. | ||||
| 
 | ||||
| FLAG: | ||||
| On self-managed GitLab, by default blocking weak passwords is not available. To make it available, ask an administrator | ||||
| to [enable the feature flag](../../administration/feature_flags.md) named `block_weak_passwords`. On GitLab.com, this | ||||
| feature is available but can be configured by GitLab.com administrators only. | ||||
| > - [Enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/363445) on GitLab.com in GitLab 15.6. | ||||
| > - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/363445) and enabled on self-managed in GitLab 15.7. Feature flag `block_weak_passwords` removed. | ||||
| 
 | ||||
| GitLab disallows weak passwords. Your password is considered weak when it: | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,9 @@ | |||
| # Each table / view needs to have assigned gitlab_schema. Names supported today: | ||||
| # | ||||
| # - gitlab_shared - defines a set of tables that are found on all databases (data accessed is dependent on connection) | ||||
| # - gitlab_main / gitlab_ci - defines a set of tables that can only exist on a given database | ||||
| # - gitlab_main / gitlab_ci - defines a set of tables that can only exist on a given application database | ||||
| # - gitlab_geo - defines a set of tables that can only exist on the geo database | ||||
| # - gitlab_internal - defines all internal tables of Rails and PostgreSQL | ||||
| # | ||||
| # Tables for the purpose of tests should be prefixed with `_test_my_table_name` | ||||
| 
 | ||||
|  | @ -55,7 +57,7 @@ module Gitlab | |||
|         tables.map { |table| table_schema(table) }.to_set | ||||
|       end | ||||
| 
 | ||||
|       def self.table_schema(name) | ||||
|       def self.table_schema(name, undefined: true) | ||||
|         schema_name, table_name = name.split('.', 2) # Strip schema name like: `public.` | ||||
| 
 | ||||
|         # Most of names do not have schemas, ensure that this is table | ||||
|  | @ -84,6 +86,8 @@ module Gitlab | |||
| 
 | ||||
|         return :gitlab_ci if table_name.start_with?('_test_gitlab_ci_') | ||||
| 
 | ||||
|         return :gitlab_geo if table_name.start_with?('_test_gitlab_geo_') | ||||
| 
 | ||||
|         # All tables that start with `_test_` without a following schema are shared and ignored | ||||
|         return :gitlab_shared if table_name.start_with?('_test_') | ||||
| 
 | ||||
|  | @ -91,7 +95,7 @@ module Gitlab | |||
|         return :gitlab_internal if table_name.start_with?('pg_') | ||||
| 
 | ||||
|         # When undefined it's best to return a unique name so that we don't incorrectly assume that 2 undefined schemas belong on the same database | ||||
|         :"undefined_#{table_name}" | ||||
|         undefined ? :"undefined_#{table_name}" : nil | ||||
|       end | ||||
| 
 | ||||
|       def self.tables_to_schema | ||||
|  |  | |||
|  | @ -51,6 +51,10 @@ module Gitlab | |||
|         include Gitlab::Database::MigrationHelpers::RestrictGitlabSchema | ||||
|       end | ||||
| 
 | ||||
|       class V2_1 < V2_0 # rubocop:disable Naming/ClassAndModuleCamelCase | ||||
|         include Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables | ||||
|       end | ||||
| 
 | ||||
|       def self.[](version) | ||||
|         version = version.to_s | ||||
|         name = "V#{version.tr('.', '_')}" | ||||
|  | @ -61,7 +65,7 @@ module Gitlab | |||
| 
 | ||||
|       # The current version to be used in new migrations | ||||
|       def self.current_version | ||||
|         2.0 | ||||
|         2.1 | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -0,0 +1,74 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Gitlab | ||||
|   module Database | ||||
|     module MigrationHelpers | ||||
|       module AutomaticLockWritesOnTables | ||||
|         extend ActiveSupport::Concern | ||||
| 
 | ||||
|         included do | ||||
|           class_attribute :skip_automatic_lock_on_writes | ||||
|         end | ||||
| 
 | ||||
|         def exec_migration(connection, direction) | ||||
|           return super if %w[main ci].exclude?(Gitlab::Database.db_config_name(connection)) | ||||
|           return super if automatic_lock_on_writes_disabled? | ||||
| 
 | ||||
|           # This compares the tables only on the `public` schema. Partitions are not affected | ||||
|           tables = connection.tables | ||||
|           super | ||||
|           new_tables = connection.tables - tables | ||||
| 
 | ||||
|           new_tables.each do |table_name| | ||||
|             lock_writes_on_table(connection, table_name) if should_lock_writes_on_table?(table_name) | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         private | ||||
| 
 | ||||
|         def automatic_lock_on_writes_disabled? | ||||
|           # Feature flags are set on the main database, see tables features/feature_gates. | ||||
|           # That is why we switch the ActiveRecord::Base.connection temporarily here back to the 'main' database | ||||
|           # for the cases when the migration is targeting another database, like the 'ci' database. | ||||
|           with_restored_connection_stack do |_| | ||||
|             Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.with_suppressed do | ||||
|               skip_automatic_lock_on_writes || | ||||
|                 Gitlab::Utils.to_boolean(ENV['SKIP_AUTOMATIC_LOCK_ON_WRITES']) || | ||||
|                 Feature.disabled?(:automatic_lock_writes_on_table, type: :ops) | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def should_lock_writes_on_table?(table_name) | ||||
|           # currently gitlab_schema represents only present existing tables, this is workaround for deleted tables | ||||
|           # that should be skipped as they will be removed in a future migration. | ||||
|           return false if Gitlab::Database::GitlabSchema::DELETED_TABLES[table_name] | ||||
| 
 | ||||
|           table_schema = Gitlab::Database::GitlabSchema.table_schema(table_name.to_s, undefined: false) | ||||
| 
 | ||||
|           if table_schema.nil? | ||||
|             error_message = <<~ERROR | ||||
|               No gitlab_schema is defined for the table #{table_name}. Please consider | ||||
|               adding it to the file config 'lib/gitlab/database/gitlab_schemas.yml' | ||||
|             ERROR | ||||
|             raise error_message | ||||
|           end | ||||
| 
 | ||||
|           return false unless %i[gitlab_main gitlab_ci].include?(table_schema) | ||||
| 
 | ||||
|           Gitlab::Database.gitlab_schemas_for_connection(connection).exclude?(table_schema) | ||||
|         end | ||||
| 
 | ||||
|         def lock_writes_on_table(connection, table_name) | ||||
|           database_name = Gitlab::Database.db_config_name(connection) | ||||
|           LockWritesManager.new( | ||||
|             table_name: table_name, | ||||
|             connection: connection, | ||||
|             database_name: database_name, | ||||
|             logger: Logger.new($stdout) | ||||
|           ).lock_writes | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -25,7 +25,23 @@ module Gitlab | |||
|         # The intention here is to not introduce an assumption about the standard schema, | ||||
|         # unless we have a good reason to do so. | ||||
|         structure.gsub!(/public\.(\w+)/, '\1') | ||||
|         structure.gsub!(/CREATE EXTENSION IF NOT EXISTS (\w+) WITH SCHEMA public;/, 'CREATE EXTENSION IF NOT EXISTS \1;') | ||||
|         structure.gsub!( | ||||
|           /CREATE EXTENSION IF NOT EXISTS (\w+) WITH SCHEMA public;/, | ||||
|           'CREATE EXTENSION IF NOT EXISTS \1;' | ||||
|         ) | ||||
| 
 | ||||
|         # Table lock-writes triggers should not be added to the schema | ||||
|         # These triggers are added by the rake task gitlab:db:lock_writes for a decomposed database. | ||||
|         structure.gsub!( | ||||
|           %r{ | ||||
|             ^CREATE.TRIGGER.gitlab_schema_write_trigger_\w+ | ||||
|             \s | ||||
|             BEFORE.INSERT.OR.DELETE.OR.UPDATE.OR.TRUNCATE.ON.\w+ | ||||
|             \s | ||||
|             FOR.EACH.STATEMENT.EXECUTE.FUNCTION.gitlab_schema_prevent_write\(\);$ | ||||
|           }x, | ||||
|           '' | ||||
|         ) | ||||
| 
 | ||||
|         structure.gsub!(/\n{3,}/, "\n\n") | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ | |||
|     "@gitlab/at.js": "1.5.7", | ||||
|     "@gitlab/favicon-overlay": "2.0.0", | ||||
|     "@gitlab/svgs": "3.11.0", | ||||
|     "@gitlab/ui": "49.11.2", | ||||
|     "@gitlab/ui": "50.1.2", | ||||
|     "@gitlab/visual-review-tools": "1.7.3", | ||||
|     "@gitlab/web-ide": "0.0.1-dev-20221114183058", | ||||
|     "@rails/actioncable": "6.1.4-7", | ||||
|  |  | |||
|  | @ -9,9 +9,10 @@ module RuboCop | |||
|         include MigrationHelpers | ||||
| 
 | ||||
|         ENFORCED_SINCE = 2021_09_02_00_00_00 | ||||
|         CURRENT_DATABASE_MIGRATION_CLASS = 'Gitlab::Database::Migration[2.1]' | ||||
| 
 | ||||
|         MSG_INHERIT = 'Don\'t inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.' | ||||
|         MSG_INCLUDE = 'Don\'t include migration helper modules directly. Inherit from Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.' | ||||
|         MSG_INHERIT = 'Don\'t inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.' | ||||
|         MSG_INCLUDE = 'Don\'t include migration helper modules directly. Inherit from Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.' | ||||
| 
 | ||||
|         ACTIVERECORD_MIGRATION_CLASS = 'ActiveRecord::Migration' | ||||
| 
 | ||||
|  |  | |||
|  | @ -486,7 +486,6 @@ RSpec.describe RegistrationsController do | |||
| 
 | ||||
|       subject { post(:create, params: new_user_params) } | ||||
| 
 | ||||
|       context 'when block_weak_passwords is enabled (default)' do | ||||
|       it 'renders the form with errors' do | ||||
|         expect { subject }.not_to change(User, :count) | ||||
| 
 | ||||
|  | @ -506,17 +505,6 @@ RSpec.describe RegistrationsController do | |||
|       end | ||||
|     end | ||||
| 
 | ||||
|       context 'when block_weak_passwords is disabled' do | ||||
|         before do | ||||
|           stub_feature_flags(block_weak_passwords: false) | ||||
|         end | ||||
| 
 | ||||
|         it 'permits weak passwords' do | ||||
|           expect { subject }.to change(User, :count).by(1) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when the password is not weak' do | ||||
|       it 'does not track a weak password error' do | ||||
|         subject | ||||
|  |  | |||
|  | @ -77,3 +77,17 @@ ALTER TABLE ONLY public.abuse_reports | |||
| 
 | ||||
| CREATE INDEX index_abuse_reports_on_user_id ON public.abuse_reports USING btree (user_id); | ||||
| 
 | ||||
| CREATE TRIGGER gitlab_schema_write_trigger_for_users BEFORE INSERT OR DELETE OR UPDATE OR TRUNCATE ON users FOR EACH STATEMENT EXECUTE FUNCTION gitlab_schema_prevent_write(); | ||||
| 
 | ||||
| CREATE FUNCTION gitlab_schema_prevent_write() RETURNS trigger | ||||
|     LANGUAGE plpgsql | ||||
|     AS $$ | ||||
| BEGIN | ||||
|     IF COALESCE(NULLIF(current_setting(CONCAT('lock_writes.', TG_TABLE_NAME), true), ''), 'true') THEN | ||||
|       RAISE EXCEPTION 'Table: "%" is write protected within this Gitlab database.', TG_TABLE_NAME | ||||
|         USING ERRCODE = 'modifying_sql_data_not_permitted', | ||||
|         HINT = 'Make sure you are using the right database connection'; | ||||
| END IF; | ||||
| RETURN NEW; | ||||
| END | ||||
| $$; | ||||
|  |  | |||
|  | @ -24,3 +24,16 @@ ALTER TABLE ONLY abuse_reports | |||
|     ADD CONSTRAINT abuse_reports_pkey PRIMARY KEY (id); | ||||
| 
 | ||||
| CREATE INDEX index_abuse_reports_on_user_id ON abuse_reports USING btree (user_id); | ||||
| 
 | ||||
| CREATE FUNCTION gitlab_schema_prevent_write() RETURNS trigger | ||||
|     LANGUAGE plpgsql | ||||
|     AS $$ | ||||
| BEGIN | ||||
|     IF COALESCE(NULLIF(current_setting(CONCAT('lock_writes.', TG_TABLE_NAME), true), ''), 'true') THEN | ||||
|       RAISE EXCEPTION 'Table: "%" is write protected within this Gitlab database.', TG_TABLE_NAME | ||||
|         USING ERRCODE = 'modifying_sql_data_not_permitted', | ||||
|         HINT = 'Make sure you are using the right database connection'; | ||||
| END IF; | ||||
| RETURN NEW; | ||||
| END | ||||
| $$; | ||||
|  |  | |||
|  | @ -4,12 +4,12 @@ import Vue from 'vue'; | |||
| import Vuex from 'vuex'; | ||||
| import { extendedWrapper } from 'helpers/vue_test_utils_helper'; | ||||
| import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue'; | ||||
| import BaseComponent from '~/cycle_analytics/components/base.vue'; | ||||
| import PathNavigation from '~/cycle_analytics/components/path_navigation.vue'; | ||||
| import StageTable from '~/cycle_analytics/components/stage_table.vue'; | ||||
| import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue'; | ||||
| import { NOT_ENOUGH_DATA_ERROR } from '~/cycle_analytics/constants'; | ||||
| import initState from '~/cycle_analytics/store/state'; | ||||
| import BaseComponent from '~/analytics/cycle_analytics/components/base.vue'; | ||||
| import PathNavigation from '~/analytics/cycle_analytics/components/path_navigation.vue'; | ||||
| import StageTable from '~/analytics/cycle_analytics/components/stage_table.vue'; | ||||
| import ValueStreamFilters from '~/analytics/cycle_analytics/components/value_stream_filters.vue'; | ||||
| import { NOT_ENOUGH_DATA_ERROR } from '~/analytics/cycle_analytics/constants'; | ||||
| import initState from '~/analytics/cycle_analytics/store/state'; | ||||
| import { | ||||
|   transformedProjectStagePathData, | ||||
|   selectedStage, | ||||
|  | @ -7,8 +7,8 @@ import { | |||
|   filterMilestones, | ||||
|   filterLabels, | ||||
| } from 'jest/vue_shared/components/filtered_search_bar/store/modules/filters/mock_data'; | ||||
| import FilterBar from '~/cycle_analytics/components/filter_bar.vue'; | ||||
| import storeConfig from '~/cycle_analytics/store'; | ||||
| import FilterBar from '~/analytics/cycle_analytics/components/filter_bar.vue'; | ||||
| import storeConfig from '~/analytics/cycle_analytics/store'; | ||||
| import * as commonUtils from '~/lib/utils/common_utils'; | ||||
| import * as urlUtils from '~/lib/utils/url_utility'; | ||||
| import { | ||||
|  | @ -1,5 +1,5 @@ | |||
| import { shallowMount } from '@vue/test-utils'; | ||||
| import Component from '~/cycle_analytics/components/formatted_stage_count.vue'; | ||||
| import Component from '~/analytics/cycle_analytics/components/formatted_stage_count.vue'; | ||||
| 
 | ||||
| describe('Formatted Stage Count', () => { | ||||
|   let wrapper = null; | ||||
|  | @ -12,7 +12,7 @@ import { | |||
|   PAGINATION_TYPE, | ||||
|   PAGINATION_SORT_DIRECTION_DESC, | ||||
|   PAGINATION_SORT_FIELD_END_EVENT, | ||||
| } from '~/cycle_analytics/constants'; | ||||
| } from '~/analytics/cycle_analytics/constants'; | ||||
| import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; | ||||
| import { getDateInPast } from '~/lib/utils/datetime_utility'; | ||||
| 
 | ||||
|  | @ -2,7 +2,7 @@ import { GlPath, GlSkeletonLoader } from '@gitlab/ui'; | |||
| import { mount } from '@vue/test-utils'; | ||||
| import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; | ||||
| import { extendedWrapper } from 'helpers/vue_test_utils_helper'; | ||||
| import Component from '~/cycle_analytics/components/path_navigation.vue'; | ||||
| import Component from '~/analytics/cycle_analytics/components/path_navigation.vue'; | ||||
| import { transformedProjectStagePathData, selectedStage } from './mock_data'; | ||||
| 
 | ||||
| describe('Project PathNavigation', () => { | ||||
|  | @ -3,8 +3,8 @@ import { shallowMount, mount } from '@vue/test-utils'; | |||
| import { nextTick } from 'vue'; | ||||
| import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; | ||||
| import { extendedWrapper } from 'helpers/vue_test_utils_helper'; | ||||
| import StageTable from '~/cycle_analytics/components/stage_table.vue'; | ||||
| import { PAGINATION_SORT_FIELD_DURATION } from '~/cycle_analytics/constants'; | ||||
| import StageTable from '~/analytics/cycle_analytics/components/stage_table.vue'; | ||||
| import { PAGINATION_SORT_FIELD_DURATION } from '~/analytics/cycle_analytics/constants'; | ||||
| import { issueEvents, issueStage, reviewStage, reviewEvents } from './mock_data'; | ||||
| 
 | ||||
| let wrapper = null; | ||||
|  | @ -1,8 +1,8 @@ | |||
| import axios from 'axios'; | ||||
| import MockAdapter from 'axios-mock-adapter'; | ||||
| import testAction from 'helpers/vuex_action_helper'; | ||||
| import * as actions from '~/cycle_analytics/store/actions'; | ||||
| import * as getters from '~/cycle_analytics/store/getters'; | ||||
| import * as actions from '~/analytics/cycle_analytics/store/actions'; | ||||
| import * as getters from '~/analytics/cycle_analytics/store/getters'; | ||||
| import httpStatusCodes from '~/lib/utils/http_status'; | ||||
| import { | ||||
|   allowedStages, | ||||
|  | @ -1,4 +1,4 @@ | |||
| import * as getters from '~/cycle_analytics/store/getters'; | ||||
| import * as getters from '~/analytics/cycle_analytics/store/getters'; | ||||
| 
 | ||||
| import { | ||||
|   allowedStages, | ||||
|  | @ -1,10 +1,10 @@ | |||
| import { useFakeDate } from 'helpers/fake_date'; | ||||
| import * as types from '~/cycle_analytics/store/mutation_types'; | ||||
| import mutations from '~/cycle_analytics/store/mutations'; | ||||
| import * as types from '~/analytics/cycle_analytics/store/mutation_types'; | ||||
| import mutations from '~/analytics/cycle_analytics/store/mutations'; | ||||
| import { | ||||
|   PAGINATION_SORT_FIELD_END_EVENT, | ||||
|   PAGINATION_SORT_DIRECTION_DESC, | ||||
| } from '~/cycle_analytics/constants'; | ||||
| } from '~/analytics/cycle_analytics/constants'; | ||||
| import { | ||||
|   selectedStage, | ||||
|   rawIssueEvents, | ||||
|  | @ -1,5 +1,5 @@ | |||
| import { mount } from '@vue/test-utils'; | ||||
| import TotalTime from '~/cycle_analytics/components/total_time.vue'; | ||||
| import TotalTime from '~/analytics/cycle_analytics/components/total_time.vue'; | ||||
| 
 | ||||
| describe('TotalTime', () => { | ||||
|   let wrapper = null; | ||||
|  | @ -4,7 +4,7 @@ import { | |||
|   formatMedianValues, | ||||
|   filterStagesByHiddenStatus, | ||||
|   buildCycleAnalyticsInitialData, | ||||
| } from '~/cycle_analytics/utils'; | ||||
| } from '~/analytics/cycle_analytics/utils'; | ||||
| import { | ||||
|   selectedStage, | ||||
|   allowedStages, | ||||
|  | @ -1,8 +1,8 @@ | |||
| import { shallowMount } from '@vue/test-utils'; | ||||
| import Daterange from '~/analytics/shared/components/daterange.vue'; | ||||
| import ProjectsDropdownFilter from '~/analytics/shared/components/projects_dropdown_filter.vue'; | ||||
| import FilterBar from '~/cycle_analytics/components/filter_bar.vue'; | ||||
| import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue'; | ||||
| import FilterBar from '~/analytics/cycle_analytics/components/filter_bar.vue'; | ||||
| import ValueStreamFilters from '~/analytics/cycle_analytics/components/value_stream_filters.vue'; | ||||
| import { | ||||
|   createdAfter as startDate, | ||||
|   createdBefore as endDate, | ||||
|  | @ -3,7 +3,7 @@ | |||
| # See https://docs.gitlab.com/ee/development/migration_style_guide.html | ||||
| # for more information on how to write migrations for GitLab. | ||||
| 
 | ||||
| class CreateModelGeneratorTestFoos < Gitlab::Database::Migration[2.0] | ||||
| class CreateModelGeneratorTestFoos < Gitlab::Database::Migration[2.1] | ||||
|   # When using the methods "add_concurrent_index" or "remove_concurrent_index" | ||||
|   # you must disable the use of transactions | ||||
|   # as these methods can not run in an existing transaction. | ||||
|  |  | |||
|  | @ -0,0 +1,333 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables, | ||||
|   :reestablished_active_record_base, query_analyzers: false do | ||||
|   using RSpec::Parameterized::TableSyntax | ||||
| 
 | ||||
|   let(:schema_class) { Class.new(Gitlab::Database::Migration[2.1]) } | ||||
|   let(:gitlab_main_table_name) { :_test_gitlab_main_table } | ||||
|   let(:gitlab_ci_table_name) { :_test_gitlab_ci_table } | ||||
|   let(:gitlab_geo_table_name) { :_test_gitlab_geo_table } | ||||
|   let(:gitlab_shared_table_name) { :_test_table } | ||||
| 
 | ||||
|   before do | ||||
|     stub_feature_flags(automatic_lock_writes_on_table: true) | ||||
|     reconfigure_db_connection(model: ActiveRecord::Base, config_model: config_model) | ||||
|   end | ||||
| 
 | ||||
|   shared_examples 'does not lock writes on table' do |config_model| | ||||
|     let(:config_model) { config_model } | ||||
| 
 | ||||
|     it 'allows deleting records from the table' do | ||||
|       allow_next_instance_of(Gitlab::Database::LockWritesManager) do |instance| | ||||
|         expect(instance).not_to receive(:lock_writes) | ||||
|       end | ||||
| 
 | ||||
|       run_migration | ||||
| 
 | ||||
|       expect do | ||||
|         migration_class.connection.execute("DELETE FROM #{table_name}") | ||||
|       end.not_to raise_error | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   shared_examples 'locks writes on table' do |config_model| | ||||
|     let(:config_model) { config_model } | ||||
| 
 | ||||
|     it 'errors on deleting' do | ||||
|       allow_next_instance_of(Gitlab::Database::LockWritesManager) do |instance| | ||||
|         expect(instance).to receive(:lock_writes).and_call_original | ||||
|       end | ||||
| 
 | ||||
|       run_migration | ||||
| 
 | ||||
|       expect do | ||||
|         migration_class.connection.execute("DELETE FROM #{table_name}") | ||||
|       end.to raise_error(ActiveRecord::StatementInvalid, /is write protected/) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   context 'when executing create_table migrations' do | ||||
|     let(:create_gitlab_main_table_migration_class) { create_table_migration(gitlab_main_table_name) } | ||||
|     let(:create_gitlab_ci_table_migration_class) { create_table_migration(gitlab_ci_table_name) } | ||||
|     let(:create_gitlab_shared_table_migration_class) { create_table_migration(gitlab_shared_table_name) } | ||||
| 
 | ||||
|     context 'when single database' do | ||||
|       let(:config_model) { Gitlab::Database.database_base_models[:main] } | ||||
| 
 | ||||
|       before do | ||||
|         skip_if_multiple_databases_are_setup | ||||
|       end | ||||
| 
 | ||||
|       it 'does not lock any newly created tables' do | ||||
|         allow_next_instance_of(Gitlab::Database::LockWritesManager) do |instance| | ||||
|           expect(instance).not_to receive(:lock_writes) | ||||
|         end | ||||
| 
 | ||||
|         create_gitlab_main_table_migration_class.migrate(:up) | ||||
|         create_gitlab_ci_table_migration_class.migrate(:up) | ||||
|         create_gitlab_shared_table_migration_class.migrate(:up) | ||||
| 
 | ||||
|         expect do | ||||
|           create_gitlab_main_table_migration_class.connection.execute("DELETE FROM #{gitlab_main_table_name}") | ||||
|           create_gitlab_ci_table_migration_class.connection.execute("DELETE FROM #{gitlab_ci_table_name}") | ||||
|           create_gitlab_shared_table_migration_class.connection.execute("DELETE FROM #{gitlab_shared_table_name}") | ||||
|         end.not_to raise_error | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when multiple databases' do | ||||
|       before do | ||||
|         skip_if_multiple_databases_not_setup | ||||
|       end | ||||
| 
 | ||||
|       let(:skip_automatic_lock_on_writes) { false } | ||||
|       let(:migration_class) { create_table_migration(table_name, skip_automatic_lock_on_writes) } | ||||
|       let(:run_migration) { migration_class.migrate(:up) } | ||||
| 
 | ||||
|       context 'for creating a gitlab_main table' do | ||||
|         let(:table_name) { gitlab_main_table_name } | ||||
| 
 | ||||
|         it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main] | ||||
|         it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci] | ||||
| 
 | ||||
|         context 'when table listed as a deleted table' do | ||||
|           before do | ||||
|             stub_const("Gitlab::Database::GitlabSchema::DELETED_TABLES", { table_name.to_s => :gitlab_main }) | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|         end | ||||
| 
 | ||||
|         context 'when the migration skips automatic locking of tables' do | ||||
|           let(:skip_automatic_lock_on_writes) { true } | ||||
| 
 | ||||
|           it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|         end | ||||
| 
 | ||||
|         context 'when the SKIP_AUTOMATIC_LOCK_ON_WRITES feature flag is set' do | ||||
|           before do | ||||
|             stub_env('SKIP_AUTOMATIC_LOCK_ON_WRITES' => 'true') | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|         end | ||||
| 
 | ||||
|         context 'when the automatic_lock_writes_on_table feature flag is disabled' do | ||||
|           before do | ||||
|             stub_feature_flags(automatic_lock_writes_on_table: false) | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'for creating a gitlab_ci table' do | ||||
|         let(:table_name) { gitlab_ci_table_name } | ||||
| 
 | ||||
|         it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|         it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:main] | ||||
| 
 | ||||
|         context 'when table listed as a deleted table' do | ||||
|           before do | ||||
|             stub_const("Gitlab::Database::GitlabSchema::DELETED_TABLES", { table_name.to_s => :gitlab_ci }) | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main] | ||||
|         end | ||||
| 
 | ||||
|         context 'when the migration skips automatic locking of tables' do | ||||
|           let(:skip_automatic_lock_on_writes) { true } | ||||
| 
 | ||||
|           it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main] | ||||
|         end | ||||
| 
 | ||||
|         context 'when the SKIP_AUTOMATIC_LOCK_ON_WRITES feature flag is set' do | ||||
|           before do | ||||
|             stub_env('SKIP_AUTOMATIC_LOCK_ON_WRITES' => 'true') | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main] | ||||
|         end | ||||
| 
 | ||||
|         context 'when the automatic_lock_writes_on_table feature flag is disabled' do | ||||
|           before do | ||||
|             stub_feature_flags(automatic_lock_writes_on_table: false) | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main] | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'for creating gitlab_shared table' do | ||||
|         let(:table_name) { gitlab_shared_table_name } | ||||
| 
 | ||||
|         it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main] | ||||
|         it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|       end | ||||
| 
 | ||||
|       context 'for creating a gitlab_geo table' do | ||||
|         before do | ||||
|           skip unless geo_configured? | ||||
|         end | ||||
| 
 | ||||
|         let(:table_name) { gitlab_geo_table_name } | ||||
| 
 | ||||
|         it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:geo] | ||||
|       end | ||||
| 
 | ||||
|       context 'for creating an unknown gitlab_schema table' do | ||||
|         let(:table_name) { :foobar } # no gitlab_schema defined | ||||
|         let(:config_model) { Gitlab::Database.database_base_models[:main] } | ||||
| 
 | ||||
|         it "raises an error about undefined gitlab_schema" do | ||||
|           expected_error_message = <<~ERROR | ||||
|               No gitlab_schema is defined for the table #{table_name}. Please consider | ||||
|               adding it to the file config 'lib/gitlab/database/gitlab_schemas.yml' | ||||
|           ERROR | ||||
| 
 | ||||
|           expect { run_migration }.to raise_error(expected_error_message) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   context 'when renaming a table' do | ||||
|     before do | ||||
|       skip_if_multiple_databases_not_setup | ||||
|       create_table_migration(old_table_name).migrate(:up) # create the table first before renaming it | ||||
|     end | ||||
| 
 | ||||
|     let(:migration_class) { rename_table_migration(old_table_name, table_name) } | ||||
|     let(:run_migration) { migration_class.migrate(:up) } | ||||
| 
 | ||||
|     context 'when a gitlab_main table' do | ||||
|       let(:old_table_name) { gitlab_main_table_name } | ||||
|       let(:table_name) { :_test_gitlab_main_new_table } | ||||
|       let(:database_base_model) { Gitlab::Database.database_base_models[:main] } | ||||
| 
 | ||||
|       it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main] | ||||
|       it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|     end | ||||
| 
 | ||||
|     context 'when a gitlab_ci table' do | ||||
|       let(:old_table_name) { gitlab_ci_table_name } | ||||
|       let(:table_name) { :_test_gitlab_ci_new_table } | ||||
|       let(:database_base_model) { Gitlab::Database.database_base_models[:ci] } | ||||
| 
 | ||||
|       it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|       it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:main] | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   context 'when reversing drop_table migrations' do | ||||
|     let(:drop_gitlab_main_table_migration_class) { drop_table_migration(gitlab_main_table_name) } | ||||
|     let(:drop_gitlab_ci_table_migration_class) { drop_table_migration(gitlab_ci_table_name) } | ||||
|     let(:drop_gitlab_shared_table_migration_class) { drop_table_migration(gitlab_shared_table_name) } | ||||
| 
 | ||||
|     context 'when single database' do | ||||
|       let(:config_model) { Gitlab::Database.database_base_models[:main] } | ||||
| 
 | ||||
|       before do | ||||
|         skip_if_multiple_databases_are_setup | ||||
|       end | ||||
| 
 | ||||
|       it 'does not lock any newly created tables' do | ||||
|         allow_next_instance_of(Gitlab::Database::LockWritesManager) do |instance| | ||||
|           expect(instance).not_to receive(:lock_writes) | ||||
|         end | ||||
| 
 | ||||
|         drop_gitlab_main_table_migration_class.connection.execute("CREATE TABLE #{gitlab_main_table_name}()") | ||||
|         drop_gitlab_ci_table_migration_class.connection.execute("CREATE TABLE #{gitlab_ci_table_name}()") | ||||
|         drop_gitlab_shared_table_migration_class.connection.execute("CREATE TABLE #{gitlab_shared_table_name}()") | ||||
| 
 | ||||
|         drop_gitlab_main_table_migration_class.migrate(:up) | ||||
|         drop_gitlab_ci_table_migration_class.migrate(:up) | ||||
|         drop_gitlab_shared_table_migration_class.migrate(:up) | ||||
| 
 | ||||
|         drop_gitlab_main_table_migration_class.migrate(:down) | ||||
|         drop_gitlab_ci_table_migration_class.migrate(:down) | ||||
|         drop_gitlab_shared_table_migration_class.migrate(:down) | ||||
| 
 | ||||
|         expect do | ||||
|           drop_gitlab_main_table_migration_class.connection.execute("DELETE FROM #{gitlab_main_table_name}") | ||||
|           drop_gitlab_ci_table_migration_class.connection.execute("DELETE FROM #{gitlab_ci_table_name}") | ||||
|           drop_gitlab_shared_table_migration_class.connection.execute("DELETE FROM #{gitlab_shared_table_name}") | ||||
|         end.not_to raise_error | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when multiple databases' do | ||||
|       before do | ||||
|         skip_if_multiple_databases_not_setup | ||||
|         migration_class.connection.execute("CREATE TABLE #{table_name}()") | ||||
|         migration_class.migrate(:up) | ||||
|       end | ||||
| 
 | ||||
|       let(:migration_class) { drop_table_migration(table_name) } | ||||
|       let(:run_migration) { migration_class.migrate(:down) } | ||||
| 
 | ||||
|       context 'for re-creating a gitlab_main table' do | ||||
|         let(:table_name) { gitlab_main_table_name } | ||||
| 
 | ||||
|         it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main] | ||||
|         it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|       end | ||||
| 
 | ||||
|       context 'for re-creating a gitlab_ci table' do | ||||
|         let(:table_name) { gitlab_ci_table_name } | ||||
| 
 | ||||
|         it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|         it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:main] | ||||
|       end | ||||
| 
 | ||||
|       context 'for re-creating a gitlab_shared table' do | ||||
|         let(:table_name) { gitlab_shared_table_name } | ||||
| 
 | ||||
|         it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main] | ||||
|         it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:ci] | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def create_table_migration(table_name, skip_lock_on_writes = false) | ||||
|     migration_class = Class.new(schema_class) do | ||||
|       class << self; attr_accessor :table_name; end | ||||
|       def change | ||||
|         create_table self.class.table_name | ||||
|       end | ||||
|     end | ||||
|     migration_class.skip_automatic_lock_on_writes = skip_lock_on_writes | ||||
|     migration_class.tap { |klass| klass.table_name = table_name } | ||||
|   end | ||||
| 
 | ||||
|   def rename_table_migration(old_table_name, new_table_name) | ||||
|     migration_class = Class.new(schema_class) do | ||||
|       class << self; attr_accessor :old_table_name, :new_table_name; end | ||||
|       def change | ||||
|         rename_table self.class.old_table_name, self.class.new_table_name | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     migration_class.tap do |klass| | ||||
|       klass.old_table_name = old_table_name | ||||
|       klass.new_table_name = new_table_name | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def drop_table_migration(table_name) | ||||
|     migration_class = Class.new(schema_class) do | ||||
|       class << self; attr_accessor :table_name; end | ||||
|       def change | ||||
|         drop_table(self.class.table_name) {} | ||||
|       end | ||||
|     end | ||||
|     migration_class.tap { |klass| klass.table_name = table_name } | ||||
|   end | ||||
| 
 | ||||
|   def geo_configured? | ||||
|     !!ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'geo') | ||||
|   end | ||||
| end | ||||
|  | @ -14,7 +14,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a | |||
| 
 | ||||
|   describe '#restrict_gitlab_migration' do | ||||
|     it 'invalid schema raises exception' do | ||||
|       expect { schema_class.restrict_gitlab_migration gitlab_schema: :gitlab_non_exisiting } | ||||
|       expect { schema_class.restrict_gitlab_migration gitlab_schema: :gitlab_non_existing } | ||||
|         .to raise_error /Unknown 'gitlab_schema:/ | ||||
|     end | ||||
| 
 | ||||
|  | @ -102,7 +102,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a | |||
|         "does add index to projects in gitlab_main and gitlab_ci" => { | ||||
|           migration: ->(klass) do | ||||
|             def change | ||||
|               # Due to running in transactin we cannot use `add_concurrent_index` | ||||
|               # Due to running in transaction we cannot use `add_concurrent_index` | ||||
|               add_index :projects, :hidden | ||||
|             end | ||||
|           end, | ||||
|  |  | |||
|  | @ -19,6 +19,15 @@ RSpec.describe Gitlab::Database::SchemaCleaner do | |||
|     expect(subject).not_to match(/public\.\w+/) | ||||
|   end | ||||
| 
 | ||||
|   it 'cleans up all the gitlab_schema_prevent_write table triggers' do | ||||
|     expect(subject).not_to match(/CREATE TRIGGER gitlab_schema_write_trigger_for_\w+/) | ||||
|     expect(subject).not_to match(/FOR EACH STATEMENT EXECUTE FUNCTION gitlab_schema_prevent_write/) | ||||
|   end | ||||
| 
 | ||||
|   it 'keeps the lock_writes trigger functions' do | ||||
|     expect(subject).to match(/CREATE FUNCTION gitlab_schema_prevent_write/) | ||||
|   end | ||||
| 
 | ||||
|   it 'cleans up the full schema as expected (blackbox test with example)' do | ||||
|     expected_schema = fixture_file(File.join('gitlab', 'database', 'structure_example_cleaned.sql')) | ||||
| 
 | ||||
|  |  | |||
|  | @ -345,24 +345,6 @@ RSpec.describe User do | |||
|       context 'check_password_weakness' do | ||||
|         let(:weak_password) { "qwertyuiop" } | ||||
| 
 | ||||
|         context 'when feature flag is disabled' do | ||||
|           before do | ||||
|             stub_feature_flags(block_weak_passwords: false) | ||||
|           end | ||||
| 
 | ||||
|           it 'does not add an error when password is weak' do | ||||
|             expect(Security::WeakPasswords).not_to receive(:weak_for_user?) | ||||
| 
 | ||||
|             user.password = weak_password | ||||
|             expect(user).to be_valid | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         context 'when feature flag is enabled' do | ||||
|           before do | ||||
|             stub_feature_flags(block_weak_passwords: true) | ||||
|           end | ||||
| 
 | ||||
|         it 'checks for password weakness when password changes' do | ||||
|           expect(Security::WeakPasswords).to receive(:weak_for_user?) | ||||
|             .with(weak_password, user).and_call_original | ||||
|  | @ -393,7 +375,6 @@ RSpec.describe User do | |||
|         end | ||||
|       end | ||||
|     end | ||||
|     end | ||||
| 
 | ||||
|     describe 'name' do | ||||
|       it { is_expected.to validate_presence_of(:name) } | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ require_relative '../../../../rubocop/cop/migration/versioned_migration_class' | |||
| RSpec.describe RuboCop::Cop::Migration::VersionedMigrationClass do | ||||
|   let(:migration) do | ||||
|     <<~SOURCE | ||||
|       class TestMigration < Gitlab::Database::Migration[1.0] | ||||
|       class TestMigration < Gitlab::Database::Migration[2.1] | ||||
|         def up | ||||
|           execute 'select 1' | ||||
|         end | ||||
|  | @ -49,23 +49,23 @@ RSpec.describe RuboCop::Cop::Migration::VersionedMigrationClass do | |||
|       it 'adds an offence if inheriting from ActiveRecord::Migration' do | ||||
|         expect_offense(<<~RUBY) | ||||
|           class MyMigration < ActiveRecord::Migration[6.1] | ||||
|           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning. | ||||
|           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning. | ||||
|           end | ||||
|         RUBY | ||||
|       end | ||||
| 
 | ||||
|       it 'adds an offence if including Gitlab::Database::MigrationHelpers directly' do | ||||
|         expect_offense(<<~RUBY) | ||||
|           class MyMigration < Gitlab::Database::Migration[1.0] | ||||
|           class MyMigration < Gitlab::Database::Migration[2.1] | ||||
|             include Gitlab::Database::MigrationHelpers | ||||
|             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't include migration helper modules directly. Inherit from Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning. | ||||
|             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't include migration helper modules directly. Inherit from Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning. | ||||
|           end | ||||
|         RUBY | ||||
|       end | ||||
| 
 | ||||
|       it 'excludes ActiveRecord classes defined inside the migration' do | ||||
|         expect_no_offenses(<<~RUBY) | ||||
|           class TestMigration < Gitlab::Database::Migration[1.0] | ||||
|           class TestMigration < Gitlab::Database::Migration[2.1] | ||||
|             class TestModel < ApplicationRecord | ||||
|             end | ||||
| 
 | ||||
|  |  | |||
|  | @ -1125,10 +1125,10 @@ | |||
|   resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.11.0.tgz#91e8e25583cddef48c0c79175203e5b0a4eaa519" | ||||
|   integrity sha512-1cJu1WXPoOHfGgv5fT3nmA9cgAQ3U1Fm/oMSVYUgBxU35R0I8W704GMLsIZwBuQ/S/Ow7WLwIkoOhLb/spNKPg== | ||||
| 
 | ||||
| "@gitlab/ui@49.11.2": | ||||
|   version "49.11.2" | ||||
|   resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-49.11.2.tgz#290bba7a3d4682365ad81747cf54a2f9927526c1" | ||||
|   integrity sha512-qu5qcl+4niYBCPIZS9ZU0i1h/IGL4ZOp4hDsEAIUFGJg9Sp0TBmwdjwKJQbvnexDS3xs1eSBzi+kQ57H+c9wQQ== | ||||
| "@gitlab/ui@50.1.2": | ||||
|   version "50.1.2" | ||||
|   resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-50.1.2.tgz#d24100b6d6fc77c7708d9139f09e2142b1292d31" | ||||
|   integrity sha512-bRzF9SkDGY2WrmIuFriFLyRMym2ydAeJB71FCXfHvhei3EWAeaiZv5oEB/a4oMN8nmt0rt4GrPQ5PpGiQKoKfQ== | ||||
|   dependencies: | ||||
|     "@popperjs/core" "^2.11.2" | ||||
|     bootstrap-vue "2.20.1" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue