diff options
19 files changed, 382 insertions, 65 deletions
diff --git a/app/assets/stylesheets/page_bundles/boards.scss b/app/assets/stylesheets/page_bundles/boards.scss index 4806f4b054b..a3ec2167b13 100644 --- a/app/assets/stylesheets/page_bundles/boards.scss +++ b/app/assets/stylesheets/page_bundles/boards.scss @@ -430,7 +430,7 @@ height: $input-height; } -.issue-boards-content { +.issue-boards-content:not(.breadcrumbs) { isolation: isolate; } diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index 25e90036a53..ede20578850 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -13,7 +13,7 @@ class LegacyDiffNote < Note validates :line_code, presence: true, line_code: true - before_create :set_diff + before_create :set_diff, unless: :skip_setting_st_diff? def discussion_class(*) LegacyDiffDiscussion @@ -90,6 +90,10 @@ class LegacyDiffNote < Note self.st_diff = diff.to_hash if diff end + def skip_setting_st_diff? + st_diff.present? && importing? + end + def diff_for_line_code attributes = { noteable_type: noteable_type, diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index b4e2d17c3e5..96002c8668a 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -27,12 +27,17 @@ class ProtectedBranch < ApplicationRecord # Check if branch name is marked as protected in the system def self.protected?(project, ref_name) return true if project.empty_repo? && project.default_branch_protected? + return false if ref_name.blank? - Rails.cache.fetch("protected_ref-#{ref_name}-#{project.cache_key}") do + Rails.cache.fetch(protected_ref_cache_key(project, ref_name)) do self.matching(ref_name, protected_refs: protected_refs(project)).present? end end + def self.protected_ref_cache_key(project, ref_name) + "protected_ref-#{project.cache_key}-#{Digest::SHA1.hexdigest(ref_name)}" + end + def self.allow_force_push?(project, ref_name) project.protected_branches.allowing_force_push.matching(ref_name).any? end diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index 165564c5666..98752345074 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -2,7 +2,7 @@ - @no_breadcrumb_container = true - @no_container = true - @content_wrapper_class = "#{@content_wrapper_class} gl-relative" -- @content_class = "js-focus-mode-board" +- @content_class = "issue-boards-content js-focus-mode-board" - is_epic_board = board.to_type == "EpicBoard" - if is_epic_board - breadcrumb_title _("Epic Boards") diff --git a/data/whats_new/2021102000001_14_04.yml b/data/whats_new/2021102000001_14_04.yml new file mode 100644 index 00000000000..6157b243cbc --- /dev/null +++ b/data/whats_new/2021102000001_14_04.yml @@ -0,0 +1,60 @@ +- title: Scheduled DAST scans + body: | + GitLab’s Dynamic Application Security Testing (DAST) now supports scheduled on-demand scans. Previously, on-demand DAST scans could only be manually triggered, which limited the usability to scans that you wanted to run immediately. With this new scheduler, you can set a DAST scan to run either once at a specific time in the future, or on a recurring basis. If adding DAST to your pipelines is not an option for your organization, or if the security or compliance regulations for your area require a scan to be scheduled, this feature provides an easy way to create a scheduled scan to meet your needs. The scan can be associated with the default branch, which allows for the results to show on the Secure Dashboard and Vulnerability list. Combined with the scan and site profiles, the scheduled on-demand scans give you quick and easy access to DAST results for your application or API, on a schedule that works for your development and security teams. + stage: Secure + self-managed: true + gitlab-com: true + packages: [Ultimate] + url: 'https://docs.gitlab.com/ee/user/application_security/dast/index.html#schedule-an-on-demand-scan' + image_url: https://about.gitlab.com/images/14_4/dast_on_demand_schedule.png + published_at: 2021-10-22 + release: 14.4 +- title: Remote Repositories for GitLab in Visual Studio Code + body: | + When working in your editor you may need to refer to another project or upstream library for additional information. When you don't have that project already cloned locally, you're forced to either leave your editor and browse the project on GitLab, or locate and then clone the project so you can browse it in your editor. Both of those tasks break your current context, introduce delays, and can take you to a less familiar interface for working with code. + + [GitLab Workflow](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow) version `3.33.0` provides an option to open a remote repository. Open the command palette and use the `GitLab: Open Remote Repository` command to find and then open a project. + + Opening a remote repository allows you to browse a read-only version of a project in your familiar VS Code environment. You can then quickly find the information you're looking for, compare an implementation, or copy a snippet you need. + stage: Create + self-managed: true + gitlab-com: true + packages: [Free, Premium, Ultimate] + url: 'https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/README.md#browse-a-repository-without-cloning' + image_url: https://img.youtube.com/vi/p4GTVx_Nd2s/hqdefault.jpg + published_at: 2021-10-22 + release: 14.4 +- title: The GitLab Operator is Generally Available + body: | + GitLab 14.4 is proud to announce the general availability of [the GitLab Operator](https://about.gitlab.com/blog/2021/10/12/open-shift-ga/), with the ability to run production instances of GitLab on Kubernetes platforms, including Red Hat OpenShift. The GitLab Operator also automates day 2 operations such as upgrading components, application reconfiguration, and autoscaling. Check out the [GitLab Operator installation documentation](https://docs.gitlab.com/charts/installation/operator.html) for additional information. + stage: Enablement + self-managed: true + gitlab-com: false + packages: [Free, Premium, Ultimate] + url: 'https://docs.gitlab.com/charts/installation/operator.html' + image_url: https://img.youtube.com/vi/sEBnuhzYD2I/hqdefault.jpg + published_at: 2021-10-22 + release: 14.4 +- title: DevOps Adoption trend graph + body: | + In GitLab 14.4, we added a new graph to group-level DevOps Adoption for trend over time. This graph shows you how groups adopt DevOps features over time and can give insights into how quickly groups are adopting additional DevOps processes. This is broken down by Dev, Sec, and Ops functionality. + stage: Manage + self-managed: true + gitlab-com: true + packages: [Ultimate] + url: 'https://docs.gitlab.com/ee/user/group/devops_adoption/#adoption-over-time' + image_url: https://about.gitlab.com/images/14_4/devops_adop_table.png + published_at: 2021-10-22 + release: 14.4 +- title: Integrated error tracking inside GitLab without a Sentry instance + body: | + Prior to GitLab 14.4, you could integrate with Sentry Error Tracking by supplying an endpoint for a Sentry backend (either self-deployed or in their cloud service). With Gitlab 14.4, you now have access to a Sentry-compatible backend built into your GitLab instance. This allows you to quickly instrument your apps so your errors show up directly in GitLab without the need for a separate Sentry instance. + stage: Manage + self-managed: true + gitlab-com: true + packages: [Free, Premium, Ultimate] + url: 'https://docs.gitlab.com/ee/operations/error_tracking.html#integrated-error-tracking' + image_url: https://about.gitlab.com/images/14_4/monitor-integrated-error-tracking.png + published_at: 2021-10-22 + release: 14.4 + diff --git a/db/post_migrate/20211004110500_add_temporary_index_to_issue_metrics.rb b/db/post_migrate/20211004110500_add_temporary_index_to_issue_metrics.rb index cfc37c55121..2c2c3ac00af 100644 --- a/db/post_migrate/20211004110500_add_temporary_index_to_issue_metrics.rb +++ b/db/post_migrate/20211004110500_add_temporary_index_to_issue_metrics.rb @@ -6,7 +6,9 @@ class AddTemporaryIndexToIssueMetrics < Gitlab::Database::Migration[1.0] INDEX_NAME = 'index_issue_metrics_first_mentioned_in_commit' def up - add_concurrent_index :issue_metrics, :issue_id, where: 'EXTRACT(YEAR FROM first_mentioned_in_commit_at) > 2019', name: INDEX_NAME + condition = Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt::TmpIssueMetrics + .first_mentioned_in_commit_at_condition + add_concurrent_index :issue_metrics, :issue_id, where: condition, name: INDEX_NAME end def down diff --git a/db/post_migrate/20211004110927_schedule_fix_first_mentioned_in_commit_at_job.rb b/db/post_migrate/20211004110927_schedule_fix_first_mentioned_in_commit_at_job.rb index c7612db3aaf..ffdb7f80f0e 100644 --- a/db/post_migrate/20211004110927_schedule_fix_first_mentioned_in_commit_at_job.rb +++ b/db/post_migrate/20211004110927_schedule_fix_first_mentioned_in_commit_at_job.rb @@ -8,8 +8,8 @@ class ScheduleFixFirstMentionedInCommitAtJob < Gitlab::Database::Migration[1.0] disable_ddl_transaction! def up - scope = define_batchable_model('issue_metrics') - .where('EXTRACT(YEAR FROM first_mentioned_in_commit_at) > 2019') + scope = Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt::TmpIssueMetrics + .from_2020 queue_background_migration_jobs_by_range_at_intervals( scope, diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md index ce5fb1aaf88..d3ea71bc8d2 100644 --- a/doc/administration/gitaly/praefect.md +++ b/doc/administration/gitaly/praefect.md @@ -1581,11 +1581,29 @@ all state associated with a given repository including: sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml remove-repository -virtual-storage <virtual-storage> -repository <repository> ``` -- `-virtual-storage` is the virtual storage the repository is located in. -- `-repository` is the repository's relative path in the storage. +- `-virtual-storage` is the virtual storage the repository is located in. Virtual storages are configured in `/etc/gitlab/gitlab.rb` under `praefect['virtual_storages]` and looks like the following: -Sometimes parts of the repository continue to exist after running `remove-repository`. This can be caused -because of: + ```ruby + praefect['virtual_storages'] = { + 'default' => { + ... + }, + 'storage-1' => { + ... + } + } + ``` + + In this example, the virtual storage to specify is `default` or `storage-1`. + +- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_types.md#hashed-storage). + For example: + + ```plaintext + @hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git + ``` + +Parts of the repository can continue to exist after running `remove-repository`. This can be because of: - A deletion error. - An in-flight RPC call targeting the repository. @@ -1609,8 +1627,53 @@ The command outputs: Each entry is a complete JSON string with a newline at the end (configurable using the `-delimiter` flag). For example: -```shell +```plaintext sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml list-untracked-repositories {"virtual_storage":"default","storage":"gitaly-1","relative_path":"@hashed/ab/cd/abcd123456789012345678901234567890123456789012345678901234567890.git"} {"virtual_storage":"default","storage":"gitaly-1","relative_path":"@hashed/ab/cd/abcd123456789012345678901234567890123456789012345678901234567891.git"} ``` + +### Manually track repositories + +> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5658) in GitLab 14.4. + +The `track-repository` Praefect sub-command adds repositories on disk to the Praefect database to be tracked. + +```shell +sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml track-repository -virtual-storage <virtual-storage> -repository <repository> +``` + +- `-virtual-storage` is the virtual storage the repository is located in. Virtual storages are configured in `/etc/gitlab/gitlab.rb` under `praefect['virtual_storages]` and looks like the following: + + ```ruby + praefect['virtual_storages'] = { + 'default' => { + ... + }, + 'storage-1' => { + ... + } + } + ``` + + In this example, the virtual storage to specify is `default` or `storage-1`. + +- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_types.md#hashed-storage). + For example: + + ```plaintext + @hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git + ``` + +- `-authoritative-storage` is the storage we want Praefect to treat as the primary. Required if + [per-repository replication](#configure-replication-factor) is set as the replication strategy. + +The command outputs: + +- Results to `STDOUT` and the command's logs. +- Errors to `STDERR`. + +This command fails if: + +- The repository is already being tracked by the Praefect database. +- The repository does not exist on disk. diff --git a/doc/install/installation.md b/doc/install/installation.md index 852ddea41bd..7c8718a7005 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -50,7 +50,7 @@ If the highest number stable branch is unclear, check the [GitLab blog](https:// | Software | Minimum version | Notes | | -------- | --------------- | ----- | | [Ruby](#2-ruby) | `2.7` | From GitLab 13.6, Ruby 2.7 is required. Ruby 3.0 is not supported yet (see [the relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/5149) for the current status). You must use the standard MRI implementation of Ruby. We love [JRuby](https://www.jruby.org/) and [Rubinius](https://github.com/rubinius/rubinius#the-rubinius-language-platform), but GitLab needs several Gems that have native extensions. | -| [Go](#3-go) | `1.15` | | +| [Go](#3-go) | `1.16` | | | [Git](#git) | `2.33.x` | From GitLab 14.4, Git 2.33.x and later is required. It's highly recommended that you use the [Git version provided by Gitaly](#git). | | [Node.js](#4-node) | `12.22.1` | GitLab uses [webpack](https://webpack.js.org/) to compile frontend assets. Node.js 14.x is recommended, as it's faster. You can check which version you're running with `node -v`. You need to update it to a newer version if needed. | @@ -251,11 +251,11 @@ page](https://golang.org/dl). # Remove former Go installation folder sudo rm -rf /usr/local/go -curl --remote-name --progress-bar "https://dl.google.com/go/go1.15.12.linux-amd64.tar.gz" -echo 'bbdb935699e0b24d90e2451346da76121b2412d30930eabcd80907c230d098b7 go1.15.12.linux-amd64.tar.gz' | shasum -a256 -c - && \ - sudo tar -C /usr/local -xzf go1.15.12.linux-amd64.tar.gz -sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ -rm go1.15.12.linux-amd64.tar.gz +curl --remote-name --progress-bar "https://golang.org/dl/go1.16.10.linux-amd64.tar.gz" +echo '414cd18ce1d193769b9e97d2401ad718755ab47816e13b2a1cde203d263b55cf go1.16.10.linux-amd64.tar.gz' | shasum -a256 -c - && \ + sudo tar -C /usr/local -xzf go1.16.10.linux-amd64.tar.gz +sudo ln -sf /usr/local/go/bin/{go,gofmt} /usr/local/bin/ +rm go1.16.10.linux-amd64.tar.gz ``` ## 4. Node diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md index 67559db2cf0..d882de62c06 100644 --- a/doc/update/upgrading_from_source.md +++ b/doc/update/upgrading_from_source.md @@ -107,12 +107,11 @@ Download and install Go (for Linux, 64-bit): # Remove former Go installation folder sudo rm -rf /usr/local/go -curl --remote-name --progress-bar "https://dl.google.com/go/go1.15.12.linux-amd64.tar.gz" -echo 'bbdb935699e0b24d90e2451346da76121b2412d30930eabcd80907c230d098b7 go1.15.12.linux-amd64.tar.gz' | shasum -a256 -c - && \ - sudo tar -C /usr/local -xzf go1.15.12.linux-amd64.tar.gz -sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ -rm go1.15.12.linux-amd64.tar.gz - +curl --remote-name --progress-bar "https://golang.org/dl/go1.16.10.linux-amd64.tar.gz" +echo '414cd18ce1d193769b9e97d2401ad718755ab47816e13b2a1cde203d263b55cf go1.16.10.linux-amd64.tar.gz' | shasum -a256 -c - && \ + sudo tar -C /usr/local -xzf go1.16.10.linux-amd64.tar.gz +sudo ln -sf /usr/local/go/bin/{go,gofmt} /usr/local/bin/ +rm go1.16.10.linux-amd64.tar.gz ``` ### 6. Update Git diff --git a/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at.rb b/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at.rb index 9b278efaedd..8f785476aa0 100644 --- a/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at.rb +++ b/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at.rb @@ -14,7 +14,15 @@ module Gitlab self.table_name = 'issue_metrics' def self.from_2020 - where('EXTRACT(YEAR FROM first_mentioned_in_commit_at) > 2019') + where(first_mentioned_in_commit_at_condition) + end + + def self.first_mentioned_in_commit_at_condition + if columns_hash['first_mentioned_in_commit_at'].sql_type == 'timestamp without time zone' + 'EXTRACT(YEAR FROM first_mentioned_in_commit_at) > 2019' + else + "EXTRACT(YEAR FROM first_mentioned_in_commit_at at time zone 'UTC') > 2019" + end end end # rubocop: enable Style/Documentation diff --git a/lib/gitlab/database/load_balancing/load_balancer.rb b/lib/gitlab/database/load_balancing/load_balancer.rb index cc9ca325337..2be7f0baa60 100644 --- a/lib/gitlab/database/load_balancing/load_balancer.rb +++ b/lib/gitlab/database/load_balancing/load_balancer.rb @@ -52,7 +52,10 @@ module Gitlab connection = host.connection return yield connection rescue StandardError => error - if serialization_failure?(error) + if primary_only? + # If we only have primary configured, retrying is pointless + raise error + elsif serialization_failure?(error) # This error can occur when a query conflicts. See # https://www.postgresql.org/docs/current/static/hot-standby.html#HOT-STANDBY-CONFLICT # for more information. diff --git a/lib/gitlab/sidekiq_middleware/size_limiter/validator.rb b/lib/gitlab/sidekiq_middleware/size_limiter/validator.rb index a83522a489a..71316bbd243 100644 --- a/lib/gitlab/sidekiq_middleware/size_limiter/validator.rb +++ b/lib/gitlab/sidekiq_middleware/size_limiter/validator.rb @@ -28,15 +28,21 @@ module Gitlab # # The worker classes aren't constants here, because that would force # Application Settings to be loaded earlier causing failures loading - # the environmant in rake tasks + # the environment in rake tasks EXEMPT_WORKER_NAMES = ["BackgroundMigrationWorker", "Database::BatchedBackgroundMigrationWorker"].to_set + JOB_STATUS_KEY = 'size_limiter' class << self def validate!(worker_class, job) return if EXEMPT_WORKER_NAMES.include?(worker_class.to_s) + return if validated?(job) new(worker_class, job).validate! end + + def validated?(job) + job.has_key?(JOB_STATUS_KEY) + end end DEFAULT_SIZE_LIMIT = 0 @@ -64,6 +70,8 @@ module Gitlab end def validate! + @job[JOB_STATUS_KEY] = 'validated' + job_args = compress_if_necessary(::Sidekiq.dump_json(@job['args'])) return if @size_limit == 0 @@ -72,8 +80,10 @@ module Gitlab exception = exceed_limit_error(job_args) if compress_mode? + @job.delete(JOB_STATUS_KEY) raise exception else + @job[JOB_STATUS_KEY] = 'tracked' track(exception) end end diff --git a/qa/qa/page/component/issue_board/show.rb b/qa/qa/page/component/issue_board/show.rb index 4b842412c0f..1c1f7ab17f3 100644 --- a/qa/qa/page/component/issue_board/show.rb +++ b/qa/qa/page/component/issue_board/show.rb @@ -48,7 +48,7 @@ module QA # with the attribute `data-qa-selector` since such element is not unique when the # `is-focused` class is not set, and it was not possible to find a better solution. def focused_board - find('.js-focus-mode-board.is-focused') + find('.issue-boards-content.js-focus-mode-board.is-focused') end def boards_dropdown diff --git a/spec/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at_spec.rb b/spec/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at_spec.rb index d2bfa86f0d1..7f15aceca42 100644 --- a/spec/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at_spec.rb +++ b/spec/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20211004110500_add_temporary_index_to_issue_metrics.rb') RSpec.describe Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt, :migration, schema: 20211004110500 do let(:namespaces) { table(:namespaces) } @@ -99,42 +100,67 @@ RSpec.describe Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt, :migrat .perform(issue_metrics.minimum(:issue_id), issue_metrics.maximum(:issue_id)) end - it "marks successful slices as completed" do - min_issue_id = issue_metrics.minimum(:issue_id) - max_issue_id = issue_metrics.maximum(:issue_id) + shared_examples 'fixes first_mentioned_in_commit_at' do + it "marks successful slices as completed" do + min_issue_id = issue_metrics.minimum(:issue_id) + max_issue_id = issue_metrics.maximum(:issue_id) - expect(subject).to receive(:mark_job_as_succeeded).with(min_issue_id, max_issue_id) + expect(subject).to receive(:mark_job_as_succeeded).with(min_issue_id, max_issue_id) - subject.perform(min_issue_id, max_issue_id) - end + subject.perform(min_issue_id, max_issue_id) + end - context 'when the persisted first_mentioned_in_commit_at is later than the first commit authored_date' do - it 'updates the issue_metrics record' do - record1 = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: Time.current) - record2 = issue_metrics.create!(issue_id: issue2.id, first_mentioned_in_commit_at: Time.current) + context 'when the persisted first_mentioned_in_commit_at is later than the first commit authored_date' do + it 'updates the issue_metrics record' do + record1 = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: Time.current) + record2 = issue_metrics.create!(issue_id: issue2.id, first_mentioned_in_commit_at: Time.current) - run_migration - record1.reload - record2.reload + run_migration + record1.reload + record2.reload - expect(record1.first_mentioned_in_commit_at).to be_within(2.seconds).of(commit2.authored_date) - expect(record2.first_mentioned_in_commit_at).to be_within(2.seconds).of(commit3.authored_date) + expect(record1.first_mentioned_in_commit_at).to be_within(2.seconds).of(commit2.authored_date) + expect(record2.first_mentioned_in_commit_at).to be_within(2.seconds).of(commit3.authored_date) + end + end + + context 'when the persisted first_mentioned_in_commit_at is earlier than the first commit authored_date' do + it 'does not update the issue_metrics record' do + record = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: 20.days.ago) + + expect { run_migration }.not_to change { record.reload.first_mentioned_in_commit_at } + end end - end - context 'when the persisted first_mentioned_in_commit_at is earlier than the first commit authored_date' do - it 'does not update the issue_metrics record' do - record = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: 20.days.ago) + context 'when the first_mentioned_in_commit_at is null' do + it 'does nothing' do + record = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: nil) - expect { run_migration }.not_to change { record.reload.first_mentioned_in_commit_at } + expect { run_migration }.not_to change { record.reload.first_mentioned_in_commit_at } + end end end - context 'when the first_mentioned_in_commit_at is null' do - it 'does nothing' do - record = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: nil) + describe 'running the migration when first_mentioned_in_commit_at is timestamp without time zone' do + it_behaves_like 'fixes first_mentioned_in_commit_at' + end + + describe 'running the migration when first_mentioned_in_commit_at is timestamp with time zone' do + around do |example| + AddTemporaryIndexToIssueMetrics.new.down + + ActiveRecord::Base.connection.execute "ALTER TABLE issue_metrics ALTER first_mentioned_in_commit_at type timestamp with time zone" + Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt::TmpIssueMetrics.reset_column_information + AddTemporaryIndexToIssueMetrics.new.up - expect { run_migration }.not_to change { record.reload.first_mentioned_in_commit_at } + example.run + + AddTemporaryIndexToIssueMetrics.new.down + ActiveRecord::Base.connection.execute "ALTER TABLE issue_metrics ALTER first_mentioned_in_commit_at type timestamp without time zone" + Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt::TmpIssueMetrics.reset_column_information + AddTemporaryIndexToIssueMetrics.new.up end + + it_behaves_like 'fixes first_mentioned_in_commit_at' end end diff --git a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb index f3ce5563e38..f824d4cefdf 100644 --- a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb +++ b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb @@ -140,6 +140,24 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do lb.read { raise conflict_error } end + context 'only primary is configured' do + let(:lb) do + config = Gitlab::Database::LoadBalancing::Configuration.new(ActiveRecord::Base) + allow(config).to receive(:load_balancing_enabled?).and_return(false) + + described_class.new(config) + end + + it 'does not retry a query on connection error if only the primary is configured' do + host = double(:host, query_cache_enabled: true) + + allow(lb).to receive(:host).and_return(host) + allow(host).to receive(:connection).and_raise(PG::UnableToSend) + + expect { lb.read }.to raise_error(PG::UnableToSend) + end + end + it 'uses the primary if no secondaries are available' do allow(lb).to receive(:connection_error?).and_return(true) diff --git a/spec/lib/gitlab/sidekiq_middleware/size_limiter/validator_spec.rb b/spec/lib/gitlab/sidekiq_middleware/size_limiter/validator_spec.rb index abbfb9cd9fa..3a6fdd7642c 100644 --- a/spec/lib/gitlab/sidekiq_middleware/size_limiter/validator_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/size_limiter/validator_spec.rb @@ -187,37 +187,51 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai context 'when size limit is 0' do let(:size_limit) { 0 } + let(:job) { job_payload(a: 'a' * 300) } it 'does not track jobs' do expect(Gitlab::ErrorTracking).not_to receive(:track_exception) - validate.call(TestSizeLimiterWorker, job_payload(a: 'a' * 300)) + validate.call(TestSizeLimiterWorker, job) end it 'does not raise exception' do expect do - validate.call(TestSizeLimiterWorker, job_payload(a: 'a' * 300)) + validate.call(TestSizeLimiterWorker, job) end.not_to raise_error end + + it 'marks the job as validated' do + validate.call(TestSizeLimiterWorker, job) + + expect(job['size_limiter']).to eq('validated') + end end context 'when job size is bigger than size limit' do let(:size_limit) { 50 } + let(:job) { job_payload(a: 'a' * 300) } it 'tracks job' do expect(Gitlab::ErrorTracking).to receive(:track_exception).with( be_a(Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError) ) - validate.call(TestSizeLimiterWorker, job_payload(a: 'a' * 100)) + validate.call(TestSizeLimiterWorker, job) end it 'does not raise an exception' do expect do - validate.call(TestSizeLimiterWorker, job_payload(a: 'a' * 300)) + validate.call(TestSizeLimiterWorker, job) end.not_to raise_error end + it 'marks the job as tracked' do + validate.call(TestSizeLimiterWorker, job) + + expect(job['size_limiter']).to eq('tracked') + end + context 'when the worker has big_payload attribute' do before do worker_class.big_payload! @@ -238,20 +252,33 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai validate.call('TestSizeLimiterWorker', job_payload(a: 'a' * 300)) end.not_to raise_error end + + it 'marks the job as validated' do + validate.call(TestSizeLimiterWorker, job) + + expect(job['size_limiter']).to eq('validated') + end end end context 'when job size is less than size limit' do let(:size_limit) { 50 } + let(:job) { job_payload(a: 'a') } it 'does not track job' do expect(Gitlab::ErrorTracking).not_to receive(:track_exception) - validate.call(TestSizeLimiterWorker, job_payload(a: 'a')) + validate.call(TestSizeLimiterWorker, job) end it 'does not raise an exception' do - expect { validate.call(TestSizeLimiterWorker, job_payload(a: 'a')) }.not_to raise_error + expect { validate.call(TestSizeLimiterWorker, job) }.not_to raise_error + end + + it 'marks the job as validated' do + validate.call(TestSizeLimiterWorker, job) + + expect(job['size_limiter']).to eq('validated') end end end @@ -266,7 +293,13 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai it 'does not raise an exception' do expect(::Gitlab::SidekiqMiddleware::SizeLimiter::Compressor).not_to receive(:compress) - expect { validate.call(TestSizeLimiterWorker, job_payload(a: 'a')) }.not_to raise_error + expect { validate.call(TestSizeLimiterWorker, job) }.not_to raise_error + end + + it 'marks the job as validated' do + validate.call(TestSizeLimiterWorker, job) + + expect(job['size_limiter']).to eq('validated') end end @@ -283,6 +316,12 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai validate.call(TestSizeLimiterWorker, job) end.not_to raise_error end + + it 'marks the job as validated' do + validate.call(TestSizeLimiterWorker, job) + + expect(job['size_limiter']).to eq('validated') + end end context 'when job size is bigger than compression threshold and size limit is 0' do @@ -299,6 +338,12 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai validate.call(TestSizeLimiterWorker, job) end.not_to raise_error end + + it 'marks the job as validated' do + validate.call(TestSizeLimiterWorker, job) + + expect(job['size_limiter']).to eq('validated') + end end context 'when the job was already compressed' do @@ -326,6 +371,8 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai expect do validate.call(TestSizeLimiterWorker, job) end.to raise_error(Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError) + + expect(job['size_limiter']).to eq(nil) end it 'does not raise an exception when the worker allows big payloads' do @@ -338,6 +385,8 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai expect do validate.call(TestSizeLimiterWorker, job) end.not_to raise_error + + expect(job['size_limiter']).to eq('validated') end end end @@ -363,6 +412,29 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai validate.call(class_name.constantize, job_payload) end end + + it "skips jobs that are already validated" do + expect(described_class).to receive(:new).once.and_call_original + + job = job_payload + + described_class.validate!(TestSizeLimiterWorker, job) + described_class.validate!(TestSizeLimiterWorker, job) + end + end + + describe '.validated?' do + let(:job) { job_payload } + + it 'returns true when the job is already validated' do + described_class.validate!(TestSizeLimiterWorker, job) + + expect(described_class.validated?(job)).to eq(true) + end + + it 'returns false when job is not yet validated' do + expect(described_class.validated?(job)).to eq(false) + end end describe '#validate!' do diff --git a/spec/models/legacy_diff_note_spec.rb b/spec/models/legacy_diff_note_spec.rb index ee3bbf186b9..8934fe6b107 100644 --- a/spec/models/legacy_diff_note_spec.rb +++ b/spec/models/legacy_diff_note_spec.rb @@ -8,4 +8,46 @@ RSpec.describe LegacyDiffNote do it { is_expected.to eq('note') } end + + describe 'callbacks' do + describe '#set_diff' do + let(:note) do + build(:legacy_diff_note_on_merge_request, st_diff: '_st_diff_').tap do |record| + record.instance_variable_set(:@diff, {}) + end + end + + context 'when not importing' do + it 'updates st_diff' do + note.save!(validate: false) + + expect(note.st_diff).to eq({}) + end + end + + context 'when importing' do + before do + note.importing = true + end + + it 'does not update st_diff' do + note.save!(validate: false) + + expect(note.st_diff).to eq('_st_diff_') + end + + context 'when st_diff is blank' do + before do + note.st_diff = nil + end + + it 'updates st_diff' do + note.save!(validate: false) + + expect(note.st_diff).to eq({}) + end + end + end + end + end end diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index 587a9683a8e..f7c723cd134 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -163,27 +163,32 @@ RSpec.describe ProtectedBranch do expect(described_class.protected?(project, 'staging/some-branch')).to eq(false) end + it 'returns false when branch name is nil' do + expect(described_class.protected?(project, nil)).to eq(false) + end + context 'with caching', :use_clean_rails_memory_store_caching do let_it_be(:project) { create(:project, :repository) } - let_it_be(:protected_branch) { create(:protected_branch, project: project, name: "jawn") } + let_it_be(:protected_branch) { create(:protected_branch, project: project, name: "“jawn”") } before do - allow(described_class).to receive(:matching).once.and_call_original + allow(described_class).to receive(:matching).with(protected_branch.name, protected_refs: anything).once.and_call_original + # the original call works and warms the cache - described_class.protected?(project, 'jawn') + described_class.protected?(project, protected_branch.name) end it 'correctly invalidates a cache' do - expect(described_class).to receive(:matching).once.and_call_original + expect(described_class).to receive(:matching).with(protected_branch.name, protected_refs: anything).once.and_call_original create(:protected_branch, project: project, name: "bar") # the cache is invalidated because the project has been "updated" - expect(described_class.protected?(project, 'jawn')).to eq(true) + expect(described_class.protected?(project, protected_branch.name)).to eq(true) end it 'correctly uses the cached version' do expect(described_class).not_to receive(:matching) - expect(described_class.protected?(project, 'jawn')).to eq(true) + expect(described_class.protected?(project, protected_branch.name)).to eq(true) end end end |